我的第一个微服务系列(二):创建 User.Api
上一篇介绍了Ocelot并且搭建了一个集成Ocelot的.Net Core API项目,只是粗浅的集成了ocelot到项目中,本篇来搭建一个用户服务并且将其添加到Api GateWay中,外部只能访问GateWay来达到访问User.Api的目的。
新建项目User.Api,在本系列中都是使用.Net Core 2.2版本构建项目。
User.Api将实现用户资料获取,用户注册,更新用户信息等功能,在项目中使用了entity framework core 2.0 做持久化工具,数据库使用MySql。
新建User和UserProperty两个实体类
public class User { public User() { Properties = new List(); } public int Id { get; set; } /// /// 用户名称 /// public string Name { get; set; } /// /// 公司名称 /// public string Company { get; set; } /// /// 职位 /// public string Title { get; set; } /// /// 手机号码 /// public string Phone { get; set; } /// /// 头像地址 /// public string Avatar { get; set; } /// /// 性别 1:男 0:女 /// public byte Gender { get; set; } /// /// 详细地址 /// public string Address { get; set; } /// /// 邮箱 /// public string Email { get; set; } /// /// 公司电话 /// public string Tel { get; set; } /// /// 省份Id /// public int ProvinceId { get; set; } /// /// 省名称 /// public string Province { get; set; } /// /// 城市Id /// public int CityId { get; set; } /// /// 城市名称 /// public string City { get; set; } /// /// 名片地址 /// public string NameCard { get; set; } /// /// 用户属性列表 /// public List Properties { get; set; } } public class UserProperty { private int? _requestedHashCode; public int UserId { get; set; } public string Key { get; set; } public string Text { get; set; } public string Value { get; set; } public override int GetHashCode() { if (!IsTransient()) { if (!_requestedHashCode.HasValue) { _requestedHashCode = (this.Key + this.Value).GetHashCode() ^ 31; } return _requestedHashCode.Value; } return base.GetHashCode(); } public override bool Equals(object obj) { if(obj == null || !(obj is UserProperty)) { return false; } if (Object.ReferenceEquals(this, obj)) { return true; } UserProperty item = obj as UserProperty; if(item.IsTransient() || this.IsTransient()) { return false; } else { return item.Key == this.Key && item.Value == this.Value; } } private bool IsTransient() { return string.IsNullOrEmpty(this.Key) || string.IsNullOrEmpty(this.Value); } public static bool operator ==(UserProperty left, UserProperty right) { if (Object.Equals(left, null)) { return (Object.Equals(right, null)) ? true : false; } else { return left.Equals(right); } } public static bool operator !=(UserProperty left, UserProperty right) { return !(left == right); } }
添加EF配置
public class UserConfiguration : IEntityTypeConfiguration{ public void Configure(EntityTypeBuilder builder) { builder.ToTable("Users"); builder.HasKey(u => u.Id); } } public class UserPropertyConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("UserProperties"); builder.HasKey(u => new { u.Key,u.UserId,u.Value }); builder.Property(u => u.Key).HasMaxLength(100); builder.Property(u => u.Value).HasMaxLength(100); } }
创建UserContext
namespace User.API.Data { public class UserContext : DbContext { public UserContext(DbContextOptionsoptions) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { //modelBuilder.Entity () // .ToTable("Users") // .HasKey(u => u.Id); modelBuilder.ApplyConfiguration(new UserConfiguration()); modelBuilder.ApplyConfiguration(new UserPropertyConfiguration()); modelBuilder.DataSeed(); base.OnModelCreating(modelBuilder); } public DbSetUsers { get; set; } public DbSet UserProperties { get; set; } } }
在Startup中注入UserContext
services.AddDbContext(options => { options.UseMySql(Configuration.GetConnectionString("MysqlUser")); });
"ConnectionStrings": { "MysqlUser": "server=172.18.6.155;port=3306;database=users;userid=root;password=123456", },
新建UserController
namespace User.API.Controllers { [Route("api/[controller]")] //[ApiController] //加了该特性,请求ContentType需要为json类型,参数可以省略掉[FromBody],但是string类型又拿不到,去掉该特性可以通过Form-Data提交数据,string类型可以获取到,但是json类型需要加[FromBody] public class UserController : BaseController { #region Member private UserContext _userContext; private ILogger_logger;#endregion #region Ctor public UserController(UserContext userContext , ILogger logger) { _userContext = userContext; _logger = logger; } #endregion #region 获取当前用户的资料 /// /// 获取当前用户的资料 /// /// [Route("")] [HttpGet] public async Task Get() { var user = await _userContext.Users .AsNoTracking() .Include(u => u.Properties) .SingleOrDefaultAsync(u => u.Id == UserIdentity.UserId); if (user == null) { throw new UserOperationException($"错误的用户上下文Id {UserIdentity.UserId}"); } return Json(user); } #endregion #region 更新用户信息 /// /// 更新用户信息,使用JsonPatch 具体操作可参考: /// [ /// { /// "op": "replace", /// "path": "/company", /// "value": "yubay.com" /// }, /// { /// "op": "replace", /// "path": "/title", /// "value": "软件工程师" /// }, /// { /// "op": "replace", /// "path": "/properties", /// "value": [{ /// "key":"fin_stage", /// "value":"A轮", /// "text":"A轮" /// },{ /// "key":"fin_stage", /// "value":"B轮", /// "text":"B轮" /// }, /// { /// "key": "fin_stage", /// "text": "C轮", /// "value": "C轮" /// }] /// } /// ] /// https://docs.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-2.2 /// /// /// [HttpPatch] public async Task Patch([FromBody]JsonPatchDocument patch) { var user = await _userContext.Users.SingleOrDefaultAsync(u => u.Id == UserIdentity.UserId); patch.ApplyTo(user); foreach (var property in user.Properties) { _userContext.Entry(property).State = EntityState.Detached; } var originProperties = await _userContext.UserProperties.AsNoTracking().Where(u => u.UserId == UserIdentity.UserId).ToListAsync(); var allProperties = originProperties.Union(user.Properties).Distinct(); var removeProperties = originProperties.Except(user.Properties); var newProperties = allProperties.Except(originProperties); foreach (var property in removeProperties) { //_userContext.Entry(property).State = EntityState.Deleted; _userContext.Remove(property); } foreach (var property in newProperties) { _userContext.Add(property); } using(var trans = _userContext.Database.BeginTransaction()) { _userContext.Users.Update(user); await _userContext.SaveChangesAsync(); trans.Commit(); } return Json(user); } #endregion #region 检查或者创建用户(当用户手机号码不存在的时候则创建用户) /// /// 检查或者创建用户(当用户手机号码不存在的时候则创建用户) /// /// /// [Route("check-or-create")] [HttpPost] public async Task CheckOrCreate(string phone) { //Todo Check Phone Valid var user = await _userContext.Users.SingleOrDefaultAsync(u => u.Phone == phone); if (user == null) { user = new Models.User { Phone = phone }; _userContext.Users.Add(user); await _userContext.SaveChangesAsync(); } return Ok(new { user.Id, user.Name, user.Company, user.Title, user.Avatar }); } #endregion #region 根据手机号码查找用户资料 /// /// 根据手机号码查找用户资料 /// /// /// [HttpPost] [Route("search")] public async Task Search(string phone) { return Json(await _userContext.Users.Include(u => u.Properties).SingleOrDefaultAsync(u => u.Phone == phone)); } #endregion [HttpGet] [Route("baseinfo/{userId}")] public async Task GetBaseInfoAsync(int userId) { //To do 检查用户 var user = await _userContext.Users.SingleOrDefaultAsync(u => u.Id == userId); if(user == null) { return NotFound(); } return Ok(new { UserId = user.Id, user.Name, user.Company, user.Title, user.Avatar }); } } }
至此,User.Api基本的功能已完成,下篇就来搭建IdentityServer认证服务,通过IdentityServer来调用User.Api实现用户认证。