Micro 开发
创建 Asp.Net Core Web应用
创建控制器文件夹(Controllers),Program.cs 配置控制器路由
1 2 3 | app.MapControllerRoute( name: "default" , pattern: "{controller=Home}/{action=Index}/{id?}" ); |
创建MVC区域文件夹(Areas),Program.cs 配置区域路由
1 2 3 | app.MapControllerRoute( name: "default" , pattern: "{controller=Home}/{action=Index}/{id?}" ); |
安装包
Ocelot.Provider.Consul
IdentityServer4.AccessTokenValidation
1.配置微服务
1)appsettings.json
1 2 3 4 5 6 | "Consul" : { "ServiceName" : "cms" , "IP" : "127.0.0.1" , "Weight" : "1" , "Port" : "5017" } |
2)启动服务 ConsulBuilderExtensions.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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
1 2 | //注册服务Consul名称 builder.Configuration.ConsulExtend(builder.Configuration.GetSection( "Consul" )[ "ServiceName" ]); |
4)创建心跳控制器 HealthController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 | namespace Micro.Cms.Controllers { [Route( "api/[controller]" )] [ApiController] public class HealthController : ControllerBase { [HttpGet( "Check" )] public ActionResult Check() { return Ok(); } } } |
2、配置统一认证 IdentityServer4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 注册认证相关组件和配置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
1 2 3 4 5 | // 注册认证过滤器,在授权过滤器前面 app.UseAuthentication(); // 注册授权过滤器 app.UseAuthorization(); |
3、配置数据上下文(AddDbContext), Program.cs
1 2 | //注入 AddDbContext 单例模式 builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString( "DefaultConnection" ))); |
2)配置数据库链接字符串
1 2 3 | "ConnectionStrings" : { "DefaultConnection" : "Server=127.0.0.1;database=cmsdb;uid=sa;pwd=mssql;TrustServerCertificate=True;" } |
4、配置允许跨域,Program.cs
1 2 3 4 5 | builder.Services.AddCors(options => { options.AddPolicy( "CustomCorsPolicy" , policy => { // 设定允许跨域的来源,有多个可以用','隔开 policy.WithOrigins( "http://api.microsoft-zh.cn" ) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); |
2)启动跨域,Program.cs
1 | app.UseCors( "CustomCorsPolicy" ); |
5、初始化日志,Program.cs
1 2 3 4 5 6 7 8 | 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
1 2 3 4 5 6 7 8 9 | 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、自定义属性验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; } } } |
调用方式:
1 2 3 4 5 6 | [HttpGet( "FormGet" )] public ActionResult FormGet([CustomValidation][FromQuery] string formCode) { var dt = _service.DataFormGet(formCode); return Ok(JsonConvert.SerializeObject(dt)); } |
8、统一输出格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | 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、异常处理中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 调用
1 2 | //异常处理中间件<br>app.UseErrorHandling(); app.UseMiddleware( typeof (ErrorHandlingMiddleware)); |
10、自定错误异常(全局)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 调用
1 2 3 4 | builder.Services.AddMvc(options => { options.Filters.Add<CustomExceptionFilter>(); }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); }; }); |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步