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);
    };
});

 

posted @ 2023-12-10 02:31  microsoft-zhcn  阅读(26)  评论(0编辑  收藏  举报