Micro 开发
创建 Asp.Net Core Web应用
创建控制器文件夹(Controllers),Program.cs 配置控制器路由
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
创建MVC区域文件夹(Areas),Program.cs 配置区域路由
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
安装包
Ocelot.Provider.Consul
IdentityServer4.AccessTokenValidation
1.配置微服务
1)appsettings.json
"Consul": { "ServiceName": "cms", "IP": "127.0.0.1", "Weight": "1", "Port": "5017" }
2)启动服务 ConsulBuilderExtensions.cs
using Consul; namespace Micro.Cms { public static class ConsulBuilderExtensions { public static void ConsulExtend(this IConfiguration configuration, string serviceName) { ConsulClient client = new(m => { //对应服务器的地址:consul的端口 m.Address = new Uri("http://127.0.0.1:8500/"); m.Datacenter = "cms"; }); //启动的时候在consul中注册实例服务 //在consul中注册的ip, port string ServiceName = configuration.GetSection("Consul")["ServiceName"]; string ip = configuration.GetSection("Consul")["IP"]; int port = int.Parse(configuration.GetSection("Consul")["Port"]); int weight = int.Parse(configuration.GetSection("Consul")["Weight"]); client.Agent.ServiceRegister(new AgentServiceRegistration() { ID = $"{configuration["ServiceName"]}-{Guid.NewGuid()}",//唯一的 Name = serviceName,//组(服务)名称(动态) Address = ip, Port = port,//不同的端口=>不同的实例 Tags = new string[] { weight.ToString() },//标签 Check = new AgentServiceCheck()//服务健康检查 { Interval = TimeSpan.FromSeconds(12),//间隔1s一次 检查 HTTP = $"http://{ip}:{port}/api/health/check", Timeout = TimeSpan.FromSeconds(5),//检测等待时间 DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)//失败后多久移除 } }); Console.WriteLine($"{ip}:{port}--weight:{weight}"); } } }
3)配置注册服务 Program.cs
//注册服务Consul名称 builder.Configuration.ConsulExtend(builder.Configuration.GetSection("Consul")["ServiceName"]);
4)创建心跳控制器 HealthController.cs
namespace Micro.Cms.Controllers { [Route("api/[controller]")] [ApiController] public class HealthController : ControllerBase { [HttpGet("Check")] public ActionResult Check() { return Ok(); } } }
2、配置统一认证 IdentityServer4
// 注册认证相关组件和配置defaultScheme为Bearer builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { // 指定要接入的授权服务器地址 options.Authority = "http://127.0.0.1:5001"; // 在验证token时,不验证Audience options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; // 不适用Https options.RequireHttpsMetadata = false; });
2)启动认证 和 授权 Program.cs
// 注册认证过滤器,在授权过滤器前面 app.UseAuthentication(); // 注册授权过滤器 app.UseAuthorization();
3、配置数据上下文(AddDbContext), Program.cs
//注入 AddDbContext 单例模式 builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
2)配置数据库链接字符串
"ConnectionStrings": { "DefaultConnection": "Server=127.0.0.1;database=cmsdb;uid=sa;pwd=mssql;TrustServerCertificate=True;" }
4、配置允许跨域,Program.cs
builder.Services.AddCors(options => { options.AddPolicy("CustomCorsPolicy", policy => { // 设定允许跨域的来源,有多个可以用','隔开 policy.WithOrigins("http://api.microsoft-zh.cn") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); });
2)启动跨域,Program.cs
app.UseCors("CustomCorsPolicy");
5、初始化日志,Program.cs
builder.Host.UseSerilog(); builder.Host.UseSerilog((builderContext, config) => { config .MinimumLevel.Warning() .Enrich.FromLogContext() .WriteTo.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day); });
6、使用Areas MVC
app.MapRazorPages(); app.MapControllerRoute( name: "areaRoute", pattern: $"{{area:exists}}/{{controller=Home}}/{{action=Index}}/{{id?}}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
7、自定义属性验证
using System.ComponentModel.DataAnnotations; namespace Micro.Bpm.Filters { /// <summary> /// 是否是英文字母、数字组合 /// </summary> public class CustomValidationAttribute : ValidationAttribute { /// <summary> /// 默认的错误提示信息 /// </summary> private const string error = "请输入正确参数信息"; protected override ValidationResult IsValid(object value, ValidationContext validationContext) { //这里是验证的参数的逻辑 value是需要验证的值 而validationContext中包含了验证相关的上下文信息 这里我是有一个自己封装的验证格式的FormatValidation类 if (!FormatValidation.IsSafeSqlString(value as string)) //不成功 提示验证错误的信息 return new ValidationResult(ErrorMessage ?? error); return ValidationResult.Success; } } }
调用方式:
[HttpGet("FormGet")] public ActionResult FormGet([CustomValidation][FromQuery] string formCode) { var dt = _service.DataFormGet(formCode); return Ok(JsonConvert.SerializeObject(dt)); }
8、统一输出格式
using Micro.Ids.Utils; using System.Net.Http; using System.Text.Json; namespace Micro { /// <summary> /// 请求响应统一消息 /// </summary> public class ResponseMessage { /// <summary> ///错误码 /// </summary> public ErrorCode Code { get; set; } /// <summary> ///错误信息 /// </summary> public object? Message { get; set; } /// <summary> ///数据信息 /// </summary> public object? Data { get; set; } /// <summary> ///请求地址 /// </summary> public string? Request { get; set; } public ResponseMessage() { } public ResponseMessage(ErrorCode errorCode) { Code = errorCode; } public ResponseMessage(ErrorCode errorCode, object message) { Code = errorCode; Message = message ?? throw new ArgumentNullException(nameof(message)); } public ResponseMessage(ErrorCode errorCode, object data, object message) { Code = errorCode; Data = data; Message = message ?? throw new ArgumentNullException(nameof(message)); } public static ResponseMessage Success(string message = "操作成功") { return new ResponseMessage(ErrorCode.Success, message); } public static ResponseMessage Success(object data, string message = "操作成功") { return new ResponseMessage(ErrorCode.Success, data, message); } public static ResponseMessage Fail(string message = "操作失败") { return new ResponseMessage(ErrorCode.Fail, message); } public static ResponseMessage Error(string message = "异常错误") { return new ResponseMessage(ErrorCode.Error, message); } /* public override string ToString() { // 小驼峰序列化 var setting = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; return JsonSerializer.Serialize(this, setting); }*/ } }
9、异常处理中间件
public class ErrorHandlingMiddleware { private readonly RequestDelegate next; public ErrorHandlingMiddleware(RequestDelegate next) { this.next = next; } public async Task Invoke(HttpContext context) { try { await next(context); } catch (Exception ex) { var statusCode = context.Response.StatusCode; if (ex is ArgumentException) { statusCode = 200; } await HandleExceptionAsync(context, statusCode, ex.Message); } finally { var statusCode = context.Response.StatusCode; var msg = ""; if (statusCode == 401) { msg = "未受权"; } else if (statusCode == 404) { msg = "未找到服务"; } else if (statusCode == 502) { msg = "请求错误"; } else if (statusCode != 200) { msg = "未知错误"; } if (!string.IsNullOrWhiteSpace(msg)) { await HandleExceptionAsync(context, statusCode, msg); } } } //异常错误信息捕获,将错误信息用Json方式返回 private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg) { var result = JsonConvert.SerializeObject(new { Success = false, Msg = msg, Type = statusCode.ToString() }); context.Response.ContentType = "application/json;charset=utf-8"; return context.Response.WriteAsync(result); } } //扩展方法 public static class ErrorHandlingExtensions { public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder) { return builder.UseMiddleware<ErrorHandlingMiddleware>(); } }
Program.cs 调用
//异常处理中间件
app.UseErrorHandling(); app.UseMiddleware(typeof(ErrorHandlingMiddleware));
10、自定错误异常(全局)
public class CustomExceptionFilter : IAsyncExceptionFilter { private readonly ILogger<CustomExceptionFilter> _logger; public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger) { _logger = logger; } public async Task OnExceptionAsync(ExceptionContext context) { _logger.LogError(context.Exception, "An error occurred."); if (!context.ExceptionHandled) { context.Result = new ViewResult { ViewName = "Error" }; context.ExceptionHandled = true; } await Task.CompletedTask; } }
Program.cs 调用
builder.Services.AddMvc(options => { options.Filters.Add<CustomExceptionFilter>(); });
builder.Services.Configure<ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = actionContext => { //获取验证失败的模型字段 var errors = actionContext.ModelState .Where(e => e.Value.Errors.Count > 0) .Select(e => e.Value.Errors.First().ErrorMessage) .ToList(); var str = string.Join("|", errors); //设置返回内容 var result = new { code = 12001, body = false, msg = str }; return new BadRequestObjectResult(result); }; });