From cb291691d56c90f639457eb1c93339e7618b3b2f Mon Sep 17 00:00:00 2001 From: xRain Date: Sat, 23 Mar 2024 07:29:43 +0800 Subject: [PATCH] upgrade to c#12 --- Attributes/SimApiAuthAttribute.cs | 89 ++--- Attributes/SimApiDocAttribute.cs | 33 +- Communications/SimApiBaseRequest.cs | 39 +- Communications/SimApiBaseResponse.cs | 113 +++--- Communications/SimApiLoginItem.cs | 13 +- Configurations/SimApiDocOptions.cs | 126 ++++--- Configurations/SimApiOptions.cs | 167 ++++----- Configurations/SimApiStorageOptions.cs | 35 +- Controllers/SimApiBaseController.cs | 124 +++--- Controllers/SimApiCommonController.cs | 87 ++--- Exceptions/SimApiException.cs | 23 +- Helpers/SimApiAuth.cs | 78 ++-- Helpers/SimApiStorage.cs | 168 ++++----- Helpers/SimApiUtil.cs | 113 +++--- Logger/SimApiLogger.cs | 52 ++- Logger/SimApiLoggerProvider.cs | 26 +- Middlewares/SimApiAuthMiddleware.cs | 42 +-- Middlewares/SimApiExceptionMiddleware.cs | 100 +++-- Models/SimApiBaseModel.cs | 152 ++++---- SimApi.csproj | 76 ++-- SimApiExtensions.cs | 457 +++++++++++------------ 21 files changed, 1013 insertions(+), 1100 deletions(-) diff --git a/Attributes/SimApiAuthAttribute.cs b/Attributes/SimApiAuthAttribute.cs index 7a652ee..1dff006 100644 --- a/Attributes/SimApiAuthAttribute.cs +++ b/Attributes/SimApiAuthAttribute.cs @@ -5,61 +5,54 @@ using Microsoft.AspNetCore.Mvc.Filters; using SimApi.Communications; using SimApi.Exceptions; -namespace SimApi.Attributes +namespace SimApi.Attributes; + +/// +/// 检测登录中间件 +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public class SimApiAuthAttribute : ActionFilterAttribute { - /// - /// 检测登录中间件 - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class SimApiAuthAttribute : ActionFilterAttribute + private string[] Types { get; } + + + //默认是user登录类型 + public SimApiAuthAttribute() { - private string[] Types { get; } + Types = new[] { "user" }; + } + //只检测一种用户类型的快捷方式 + public SimApiAuthAttribute(string type) + { + Types = type.Split(","); + } - //默认是user登录类型 - public SimApiAuthAttribute() + //设定特定类型的检测 + public SimApiAuthAttribute(string[] types) + { + Types = types; + } + + //只检测一种用户类型的快捷方式 + public SimApiAuthAttribute(string type, string url) + { + Types = new[] { type }; + new HttpPostAttribute(url); + } + + public override void OnActionExecuting(ActionExecutingContext context) + { + var loginInfo = (SimApiLoginItem)context.HttpContext.Items["LoginInfo"]; + //检测是否登录 + if (loginInfo == null) { - Types = new[] - { - "user" - }; + throw new SimApiException(401); } - - //只检测一种用户类型的快捷方式 - public SimApiAuthAttribute(string type) + //检测用户类型 + if (!Types.Intersect(loginInfo.Type).Any()) { - Types = type.Split(","); - } - - //设定特定类型的检测 - public SimApiAuthAttribute(string[] types) - { - Types = types; - } - - //只检测一种用户类型的快捷方式 - public SimApiAuthAttribute(string type, string url) - { - Types = new[] - { - type - }; - new HttpPostAttribute(url); - } - - public override void OnActionExecuting(ActionExecutingContext context) - { - var loginInfo = (SimApiLoginItem)context.HttpContext.Items["LoginInfo"]; - //检测是否登录 - if (loginInfo == null) - { - throw new SimApiException(401); - } - //检测用户类型 - if (!Types.Intersect(loginInfo.Type).Any()) - { - throw new SimApiException(403); - } + throw new SimApiException(403); } } } \ No newline at end of file diff --git a/Attributes/SimApiDocAttribute.cs b/Attributes/SimApiDocAttribute.cs index 534a0bb..84a1171 100644 --- a/Attributes/SimApiDocAttribute.cs +++ b/Attributes/SimApiDocAttribute.cs @@ -1,25 +1,24 @@ -using System; +using System; using Swashbuckle.AspNetCore.Annotations; -namespace SimApi.Attributes +namespace SimApi.Attributes; + +/// +/// 快捷自定义接口文档类 +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public class SimApiDocAttribute : SwaggerOperationAttribute { /// - /// 快捷自定义接口文档类 + /// 定义接口说明 /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] - public class SimApiDocAttribute : SwaggerOperationAttribute + /// 接口分组 + /// 接口名称 + public SimApiDocAttribute(string tag, string name) { - /// - /// 定义接口说明 - /// - /// 接口分组 - /// 接口名称 - public SimApiDocAttribute(string tag, string name) - { - Tags = new[] { tag }; - Summary = name; - // Consumes = new[] {"application/json"}; - // Produces = new[] {"application/json"}; - } + Tags = new[] { tag }; + Summary = name; + // Consumes = new[] {"application/json"}; + // Produces = new[] {"application/json"}; } } \ No newline at end of file diff --git a/Communications/SimApiBaseRequest.cs b/Communications/SimApiBaseRequest.cs index ba55ccd..d84f76b 100644 --- a/Communications/SimApiBaseRequest.cs +++ b/Communications/SimApiBaseRequest.cs @@ -1,23 +1,22 @@ -namespace SimApi.Communications -{ - /// - /// 只有ID的请求 - /// - public record SimApiIdOnlyRequest(int Id); +namespace SimApi.Communications; - /// - /// 只有ID的请求(字符串) - /// - public record SimApiStringIdOnlyRequest(string Id); +/// +/// 只有ID的请求 +/// +public record SimApiIdOnlyRequest(int Id); - /// - /// 动态类型单字段请求 - /// - /// - public record SimApiOneFieldRequest(T Data); +/// +/// 只有ID的请求(字符串) +/// +public record SimApiStringIdOnlyRequest(string Id); - /// - /// 基础分页请求 - /// - public record SimApiBasePageRequest(int Page, int Count); -} \ No newline at end of file +/// +/// 动态类型单字段请求 +/// +/// +public record SimApiOneFieldRequest(T Data); + +/// +/// 基础分页请求 +/// +public record SimApiBasePageRequest(int Page, int Count); \ No newline at end of file diff --git a/Communications/SimApiBaseResponse.cs b/Communications/SimApiBaseResponse.cs index 00b70f1..5ff9d68 100644 --- a/Communications/SimApiBaseResponse.cs +++ b/Communications/SimApiBaseResponse.cs @@ -2,75 +2,64 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace SimApi.Communications +namespace SimApi.Communications; + +/// +/// 基础相应 +/// +public record SimApiBaseResponse(int Code = 200, string Message = "成功") { /// - /// 基础相应 + /// 默认错误代码对应提示信息 /// - public record SimApiBaseResponse(int Code = 200, string Message = "成功") + private static readonly Dictionary MsgBox = new() { - /// - /// 默认错误代码对应提示信息 - /// - private static readonly Dictionary MsgBox = new() - { - { - 200, "成功" - }, - { - 204, "没有数据" - }, - { - 400, "参数错误" - }, - { - 401, "需要登录" - }, - { - 403, "无权访问" - }, - { - 404, "接口不存在" - }, - { - 500, "服务器错误" - } - }; + { 200, "成功" }, + { 204, "没有数据" }, + { 400, "参数错误" }, + { 401, "需要登录" }, + { 403, "无权访问" }, + { 404, "接口不存在" }, + { 500, "服务器错误" } + }; - public SimApiBaseResponse(int code) : this(code, MsgBox.ContainsKey(code) ? MsgBox[code] : "未知错误") - { - } - - /// - /// 序列化为JSON字符串 - /// - /// - public override string ToString() - { - return JsonSerializer.Serialize(this, new JsonSerializerOptions - { - IgnoreReadOnlyProperties = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - Converters = - { - new JsonStringEnumConverter() - } - }); - } + public SimApiBaseResponse(int code) : this(code, MsgBox.ContainsKey(code) ? MsgBox[code] : "未知错误") + { } /// - /// 动态内容分页 + /// 序列化为JSON字符串 /// - /// - public record SimApiBasePageResponse - (T List, int Page = 1, int Count = 1, int Total = 1, int Code = 200, - string Message = "成功") : SimApiBaseResponse(Code, Message); + /// + public override string ToString() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions + { + IgnoreReadOnlyProperties = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Converters = + { + new JsonStringEnumConverter() + } + }); + } +} - /// - /// 动态Data返回 - /// - /// - public record SimApiBaseResponse(T Data, int Code = 200, string Message = "成功") : SimApiBaseResponse(Code, - Message); -} \ No newline at end of file +/// +/// 动态内容分页 +/// +/// +public record SimApiBasePageResponse( + T List, + int Page = 1, + int Count = 1, + int Total = 1, + int Code = 200, + string Message = "成功") : SimApiBaseResponse(Code, Message); + +/// +/// 动态Data返回 +/// +/// +public record SimApiBaseResponse(T Data, int Code = 200, string Message = "成功") : SimApiBaseResponse(Code, + Message); \ No newline at end of file diff --git a/Communications/SimApiLoginItem.cs b/Communications/SimApiLoginItem.cs index 7552735..8c703ec 100644 --- a/Communications/SimApiLoginItem.cs +++ b/Communications/SimApiLoginItem.cs @@ -1,7 +1,6 @@ -namespace SimApi.Communications -{ - /// - /// 登录信息中间件 - /// - public record SimApiLoginItem(string Id, string[] Type); -} \ No newline at end of file +namespace SimApi.Communications; + +/// +/// 登录信息中间件 +/// +public record SimApiLoginItem(string Id, string[] Type); \ No newline at end of file diff --git a/Configurations/SimApiDocOptions.cs b/Configurations/SimApiDocOptions.cs index 00a1e1e..2f833cc 100644 --- a/Configurations/SimApiDocOptions.cs +++ b/Configurations/SimApiDocOptions.cs @@ -1,73 +1,75 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Swashbuckle.AspNetCore.SwaggerUI; -namespace SimApi.Configs +namespace SimApi.Configs; + +/// +/// 文档组配置 +/// +public class SimApiDocGroupOption +{ + /// + /// 文档标识 + /// + public string Id { get; set; } + + /// + /// 文档名称 + /// + public string Name { get; set; } + + /// + /// 文档描述 + /// + public string Description { get; set; } +} + +/// +/// 授权配置, Type支持 "SimApiAuth","ClientCredentials","Implicit","AuthorizationCode" +/// +public class SimApiAuthOption +{ + public string[] Type { get; set; } = new[] { "SimApiAuth" }; + + public string Description { get; set; } = "认证服务器颁发的AccessToken"; + + public string AuthorizationUrl { get; set; } + + public string TokenUrl { get; set; } + + public Dictionary Scopes { get; set; } +} + +/// +/// 文档配置 +/// +public class SimApiDocOptions { /// /// 文档组配置 /// - public class SimApiDocGroupOption + public SimApiDocGroupOption[] ApiGroups { get; set; } = new[] { - /// - /// 文档标识 - /// - public string Id { get; set; } - - /// - /// 文档名称 - /// - public string Name { get; set; } - - /// - /// 文档描述 - /// - public string Description { get; set; } - } - - /// - /// 授权配置, Type支持 "SimApiAuth","ClientCredentials","Implicit","AuthorizationCode" - /// - public class SimApiAuthOption - { - public string[] Type { get; set; } = new[] {"SimApiAuth"}; - public string Description { get; set; } = "认证服务器颁发的AccessToken"; - public string AuthorizationUrl { get; set; } - public string TokenUrl { get; set; } - public Dictionary Scopes { get; set; } - } - - /// - /// 文档配置 - /// - public class SimApiDocOptions - { - /// - /// 文档组配置 - /// - public SimApiDocGroupOption[] ApiGroups { get; set; } = new[] + new SimApiDocGroupOption { - new SimApiDocGroupOption - { - Id = "api", - Name = "Api", - Description = "Api接口文档" - } - }; + Id = "api", + Name = "Api", + Description = "Api接口文档" + } + }; - /// - /// 授权配置 - /// - public SimApiAuthOption ApiAuth { get; set; } = new SimApiAuthOption(); + /// + /// 授权配置 + /// + public SimApiAuthOption ApiAuth { get; set; } = new SimApiAuthOption(); - /// - /// 文档页面标题 - /// - public string DocumentTitle { get; set; } = "API接口文档"; - - /// - /// 接口支持的调用方式 - /// - public SubmitMethod[] SupportedMethod { get; set; } = new[] {SubmitMethod.Post}; - } + /// + /// 文档页面标题 + /// + public string DocumentTitle { get; set; } = "API接口文档"; + + /// + /// 接口支持的调用方式 + /// + public SubmitMethod[] SupportedMethod { get; set; } = new[] { SubmitMethod.Post }; } \ No newline at end of file diff --git a/Configurations/SimApiOptions.cs b/Configurations/SimApiOptions.cs index 678fc66..31e8e58 100644 --- a/Configurations/SimApiOptions.cs +++ b/Configurations/SimApiOptions.cs @@ -1,93 +1,92 @@ using System; -namespace SimApi.Configs +namespace SimApi.Configs; + +public class SimApiOptions { - public class SimApiOptions + /// + /// 启用全部Cors,对于开发前后分离的时候很有用。 + /// default: true + /// + public bool EnableCors { get; set; } = true; + + /// + /// 启用SimapiAuth,一个简单的基于Header Token的认证方式。 + /// default: false + /// + public bool EnableSimApiAuth { get; set; } = false; + + /// + /// 启用在线文档,启用后 访问 /swagger 可以查看对应的api文档。 + /// default: false + /// + public bool EnableSimApiDoc { get; set; } = false; + + /// + /// 启用异常拦截,启用后,所有的异常将被通过json反馈。 + /// default: true + /// + public bool EnableSimApiException { get; set; } = true; + + /// + /// 开启S3兼容的存储系统。 + /// default: false + /// + public bool EnableSimApiStorage { get; set; } = false; + + /// + /// 开启ForwardHeaders,开启后可以透传负载均衡的Headers + /// default: true + /// + public bool EnableForwardHeaders { get; set; } = true; + + /// + /// 将所有的url都格式化为小写字母 + /// default: true + /// + public bool EnableLowerUrl { get; set; } = true; + + /// + /// 启用格式化的 Console Logger + /// default: false + /// + public bool EnableLogger { get; set; } = false; + + /// + /// 是否启用Synapse + /// + public bool EnableSynapse { get; set; } = false; + + + /// + /// Swagger文档相关配置,需要启用 EnableSimApiDoc + /// + public SimApiDocOptions SimApiDocOptions { get; set; } = new(); + + /// + /// S3兼容的存储系统配置,需要启用 EnableSimApiStorage + /// + public SimApiStorageOptions SimApiStorageOptions { get; set; } = new(); + + public SimApiSynapseOptions SimApiSynapseOptions { get; set; } = new(); + + public void ConfigureSimApiSynapse(Action options = null) { - /// - /// 启用全部Cors,对于开发前后分离的时候很有用。 - /// default: true - /// - public bool EnableCors { get; set; } = true; + options?.Invoke(SimApiSynapseOptions); + } - /// - /// 启用SimapiAuth,一个简单的基于Header Token的认证方式。 - /// default: false - /// - public bool EnableSimApiAuth { get; set; } = false; + public void ConfigureSimApiSynapse(SimApiSynapseOptions options) + { + SimApiSynapseOptions = options; + } - /// - /// 启用在线文档,启用后 访问 /swagger 可以查看对应的api文档。 - /// default: false - /// - public bool EnableSimApiDoc { get; set; } = false; + public void ConfigureSimApiDoc(Action options = null) + { + options?.Invoke(SimApiDocOptions); + } - /// - /// 启用异常拦截,启用后,所有的异常将被通过json反馈。 - /// default: true - /// - public bool EnableSimApiException { get; set; } = true; - - /// - /// 开启S3兼容的存储系统。 - /// default: false - /// - public bool EnableSimApiStorage { get; set; } = false; - - /// - /// 开启ForwardHeaders,开启后可以透传负载均衡的Headers - /// default: true - /// - public bool EnableForwardHeaders { get; set; } = true; - - /// - /// 将所有的url都格式化为小写字母 - /// default: true - /// - public bool EnableLowerUrl { get; set; } = true; - - /// - /// 启用格式化的 Console Logger - /// default: false - /// - public bool EnableLogger { get; set; } = false; - - /// - /// 是否启用Synapse - /// - public bool EnableSynapse { get; set; } = false; - - - /// - /// Swagger文档相关配置,需要启用 EnableSimApiDoc - /// - public SimApiDocOptions SimApiDocOptions { get; set; } = new(); - - /// - /// S3兼容的存储系统配置,需要启用 EnableSimApiStorage - /// - public SimApiStorageOptions SimApiStorageOptions { get; set; } = new(); - - public SimApiSynapseOptions SimApiSynapseOptions { get; set; } = new(); - - public void ConfigureSimApiSynapse(Action options = null) - { - options?.Invoke(SimApiSynapseOptions); - } - - public void ConfigureSimApiSynapse(SimApiSynapseOptions options) - { - SimApiSynapseOptions = options; - } - - public void ConfigureSimApiDoc(Action options = null) - { - options?.Invoke(SimApiDocOptions); - } - - public void ConfigureSimApiStorage(Action options = null) - { - options?.Invoke(SimApiStorageOptions); - } + public void ConfigureSimApiStorage(Action options = null) + { + options?.Invoke(SimApiStorageOptions); } } \ No newline at end of file diff --git a/Configurations/SimApiStorageOptions.cs b/Configurations/SimApiStorageOptions.cs index 92ade77..d3203cb 100644 --- a/Configurations/SimApiStorageOptions.cs +++ b/Configurations/SimApiStorageOptions.cs @@ -1,24 +1,23 @@ -namespace SimApi.Configs +namespace SimApi.Configs; + +public class SimApiStorageOptions { - public class SimApiStorageOptions - { - /// - /// S3 服务器入口地址 - /// - public string Endpoint { get; set; } + /// + /// S3 服务器入口地址 + /// + public string Endpoint { get; set; } - /// - /// S3 服务器文件访问地址 - /// - public string ServeUrl { get; set; } + /// + /// S3 服务器文件访问地址 + /// + public string ServeUrl { get; set; } - /// - /// S3服务 Bucket - /// - public string Bucket { get; set; } + /// + /// S3服务 Bucket + /// + public string Bucket { get; set; } - public string AccessKey { get; set; } + public string AccessKey { get; set; } - public string SecretKey { get; set; } - } + public string SecretKey { get; set; } } \ No newline at end of file diff --git a/Controllers/SimApiBaseController.cs b/Controllers/SimApiBaseController.cs index b126469..80ea814 100644 --- a/Controllers/SimApiBaseController.cs +++ b/Controllers/SimApiBaseController.cs @@ -4,84 +4,82 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Linq; using SimApi.Exceptions; -using SimApi.Attributes; -namespace SimApi.Controllers +namespace SimApi.Controllers; + +/// +/// 基础控制器,所有控制器均继承本控制器 +/// 1. 自动验证请求参数 +/// 2. 报错返回 +/// 3. 错误回馈页面 +/// +public class SimApiBaseController : Controller { /// - /// 基础控制器,所有控制器均继承本控制器 - /// 1. 自动验证请求参数 - /// 2. 报错返回 - /// 3. 错误回馈页面 + /// 当前登录用户的ID /// - public class SimApiBaseController : Controller + protected SimApiLoginItem LoginInfo => (SimApiLoginItem)HttpContext.Items["LoginInfo"]; + + /// + /// 验证请求参数 + /// + /// + public override void OnActionExecuting(ActionExecutingContext context) { - /// - /// 当前登录用户的ID - /// - protected SimApiLoginItem LoginInfo => (SimApiLoginItem)HttpContext.Items["LoginInfo"]; - - /// - /// 验证请求参数 - /// - /// - public override void OnActionExecuting(ActionExecutingContext context) + if (!context.ModelState.IsValid) { - if (!context.ModelState.IsValid) + foreach (var item in context.ModelState.Values.Where(item => + item.ValidationState == ModelValidationState.Invalid)) { - foreach (var item in context.ModelState.Values.Where(item => - item.ValidationState == ModelValidationState.Invalid)) - { - Error(400, item.Errors.First().ErrorMessage); - break; - } + Error(400, item.Errors.First().ErrorMessage); + break; } } + } - /// - /// 错误返回 - /// - /// 错误代码 - /// 错误描述(若是常规错误,代码可自动带取描述) - /// - protected static void Error(int code = 500, string message = "") - { - throw new SimApiException(code, message); - } + /// + /// 错误返回 + /// + /// 错误代码 + /// 错误描述(若是常规错误,代码可自动带取描述) + /// + protected static void Error(int code = 500, string message = "") + { + throw new SimApiException(code, message); + } - /// - /// 检测条件,根据条件返回报错 - /// - /// 检测条件 - /// 错误代码 - /// 错误描述 - protected static void ErrorWhen(bool condition, int code = 500, string message = "") + /// + /// 检测条件,根据条件返回报错 + /// + /// 检测条件 + /// 错误代码 + /// 错误描述 + protected static void ErrorWhen(bool condition, int code = 500, string message = "") + { + if (condition) { - if (condition) - { - Error(code, message); - } + Error(code, message); } + } - /// - /// 检测给定的变量是否为NUll - /// - /// 检测条件 - /// 错误代码 - /// 错误描述 - protected static void ErrorWhenNull(object condition, int code = 404, string message = "") - { - ErrorWhen(condition == null, code, message); - } + /// + /// 检测给定的变量是否为NUll + /// + /// 检测条件 + /// 错误代码 + /// 错误描述 + protected static void ErrorWhenNull(object condition, int code = 404, string message = "") + { + ErrorWhen(condition == null, code, message); + } - /// - /// 上传文件 - /// - /// - protected SimApiBaseResponse UploadFile() - { - return new SimApiBaseResponse(null); - } + /// + /// 上传文件 + /// + /// + protected SimApiBaseResponse UploadFile() + { + return new SimApiBaseResponse(null); } } \ No newline at end of file diff --git a/Controllers/SimApiCommonController.cs b/Controllers/SimApiCommonController.cs index 3e48f55..486beab 100644 --- a/Controllers/SimApiCommonController.cs +++ b/Controllers/SimApiCommonController.cs @@ -1,60 +1,51 @@ -using System; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using SimApi.Attributes; using SimApi.Communications; using SimApi.Helpers; -namespace SimApi.Controllers +namespace SimApi.Controllers; + +[ApiExplorerSettings(GroupName = "api")] +public class SimApiCommonController(SimApiAuth auth) : SimApiBaseController { - [ApiExplorerSettings(GroupName = "api")] - public class YYCommonController : SimApiBaseController + /// + /// 错误回馈页面 + /// + /// 错误代码 + /// + [HttpGet("exception/{code:int}")] + [ApiExplorerSettings(IgnoreApi = true)] + public SimApiBaseResponse ExceptionHandler(int code) { - private SimApiAuth Auth { get; } + return new SimApiBaseResponse(code); + } - public YYCommonController(SimApiAuth auth) + /// + /// 检测用户登陆的控制器 + /// + /// + [HttpPost("/auth/check"), SimApiDoc("认证", "检测登陆")] + public SimApiBaseResponse CheckLogin() + { + ErrorWhenNull(LoginInfo, 401); + return new SimApiBaseResponse(LoginInfo.Id); + } + + /// + /// 退出登陆 + /// + /// + [HttpPost("/auth/logout"), SimApiDoc("认证", "退出登陆")] + public SimApiBaseResponse Logout() + { + string token = null; + + if (Request.Headers.ContainsKey("Token")) { - Auth = auth; + token = Request.Headers["Token"]; } - /// - /// 错误回馈页面 - /// - /// 错误代码 - /// - [HttpGet("exception/{code:int}")] - [ApiExplorerSettings(IgnoreApi = true)] - public SimApiBaseResponse ExceptionHandler(int code) - { - return new SimApiBaseResponse(code); - } - - /// - /// 检测用户登陆的控制器 - /// - /// - [HttpPost("/auth/check"), SimApiDoc("认证", "检测登陆")] - public SimApiBaseResponse CheckLogin() - { - ErrorWhenNull(LoginInfo, 401); - return new SimApiBaseResponse(LoginInfo.Id); - } - - /// - /// 退出登陆 - /// - /// - [HttpPost("/auth/logout"), SimApiDoc("认证", "退出登陆")] - public SimApiBaseResponse Logout() - { - string token = null; - - if (Request.Headers.ContainsKey("Token")) - { - token = Request.Headers["Token"]; - } - - Auth.Logout(token); - return new SimApiBaseResponse(); - } + auth.Logout(token); + return new SimApiBaseResponse(); } } \ No newline at end of file diff --git a/Exceptions/SimApiException.cs b/Exceptions/SimApiException.cs index b3cd116..cae9202 100644 --- a/Exceptions/SimApiException.cs +++ b/Exceptions/SimApiException.cs @@ -1,16 +1,11 @@ using System; -namespace SimApi.Exceptions -{ - /// - /// Api错误捕获异常 - /// - public class SimApiException : Exception - { - public int Code { get; } - public SimApiException(int code, string message = "") : base(message) - { - Code = code; - } - } -} +namespace SimApi.Exceptions; + +/// +/// Api错误捕获异常 +/// +public class SimApiException(int code, string message = "") : Exception(message) +{ + public int Code { get; } = code; +} \ No newline at end of file diff --git a/Helpers/SimApiAuth.cs b/Helpers/SimApiAuth.cs index f5ec70d..f529518 100644 --- a/Helpers/SimApiAuth.cs +++ b/Helpers/SimApiAuth.cs @@ -1,60 +1,48 @@ using System; using System.Text.Json; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Distributed; using SimApi.Communications; -namespace SimApi.Helpers +namespace SimApi.Helpers; + +/// +/// 认证助手 +/// +public class SimApiAuth(IDistributedCache cache) { /// - /// 认证助手 + /// 产生一个Token记录并返回Token /// - public class SimApiAuth + /// + /// + public string Login(string id, string type = "user", string token = null) { - private IDistributedCache Cache { get; } + return Login(id, new[] { type }, token); + } - public SimApiAuth(IDistributedCache cache) - { - Cache = cache; - } + /// + /// 产生一个Token并记录用户ID角色[多角色] + /// + /// + /// + /// + public string Login(string id, string[] type, string uuid = null) + { + uuid ??= Guid.NewGuid().ToString(); + var loginItem = new SimApiLoginItem(id, type); + cache.SetString(uuid, JsonSerializer.Serialize(loginItem)); + return uuid; + } - /// - /// 产生一个Token记录并返回Token - /// - /// - /// - public string Login(string id, string type = "user", string token = null) + /// + /// 退出登陆 + /// + /// 登陆标识 + public void Logout(string uuid) + { + if (!string.IsNullOrEmpty(uuid)) { - return Login(id, new[] - { - type - }, token); - } - - /// - /// 产生一个Token并记录用户ID角色[多角色] - /// - /// - /// - /// - public string Login(string id, string[] type, string uuid = null) - { - uuid ??= Guid.NewGuid().ToString(); - var loginItem = new SimApiLoginItem(id, type); - Cache.SetString(uuid, JsonSerializer.Serialize(loginItem)); - return uuid; - } - - /// - /// 退出登陆 - /// - /// 登陆标识 - public void Logout(string uuid) - { - if (!string.IsNullOrEmpty(uuid)) - { - Cache.Remove(uuid); - } + cache.Remove(uuid); } } } \ No newline at end of file diff --git a/Helpers/SimApiStorage.cs b/Helpers/SimApiStorage.cs index 11a73b0..53884d7 100644 --- a/Helpers/SimApiStorage.cs +++ b/Helpers/SimApiStorage.cs @@ -2,102 +2,100 @@ using System.IO; using Microsoft.AspNetCore.Http; using Minio; -using Minio.Exceptions; using SimApi.Configs; -namespace SimApi.Helpers +namespace SimApi.Helpers; + +public class SimApiStorage { - public class SimApiStorage + private MinioClient Mc { get; } + + public MinioClient Client => Mc; + + private string ServeUrl { get; } + + public string Bucket { get; } + + private IHttpContextAccessor HttpContextAccessor { get; } + + public SimApiStorage(SimApiOptions apiOptions, IHttpContextAccessor httpContextAccessor) { - private MinioClient Mc { get; } - - public MinioClient Client => Mc; - - private string ServeUrl { get; } - - public string Bucket { get; } - - private IHttpContextAccessor HttpContextAccessor { get; } - - public SimApiStorage(SimApiOptions apiOptions, IHttpContextAccessor httpContextAccessor) + var options = apiOptions.SimApiStorageOptions; + HttpContextAccessor = httpContextAccessor; + var useSsl = false; + string endpoint; + if (options.Endpoint.StartsWith("http://")) { - var options = apiOptions.SimApiStorageOptions; - HttpContextAccessor = httpContextAccessor; - var useSsl = false; - string endpoint; - if (options.Endpoint.StartsWith("http://")) - { - endpoint = options.Endpoint.Replace("http://", string.Empty); - } - else if (options.Endpoint.StartsWith("https://")) - { - endpoint = options.Endpoint.Replace("https://", string.Empty); - useSsl = true; - } - else - { - throw new Exception("SimApiStorage: Error Endpoint"); - } - - ServeUrl = options.ServeUrl; - Bucket = options.Bucket; - var mcb = new MinioClient().WithEndpoint(endpoint) - .WithCredentials(options.AccessKey, options.SecretKey); - if (useSsl) - { - mcb = mcb.WithSSL(); - } - Mc = mcb.Build(); - - var found = Mc.BucketExistsAsync(new BucketExistsArgs().WithBucket(Bucket)).Result; - if (!found) - { - Mc.MakeBucketAsync(new MakeBucketArgs().WithBucket(Bucket)).Wait(); - } + endpoint = options.Endpoint.Replace("http://", string.Empty); + } + else if (options.Endpoint.StartsWith("https://")) + { + endpoint = options.Endpoint.Replace("https://", string.Empty); + useSsl = true; + } + else + { + throw new Exception("SimApiStorage: Error Endpoint"); } - /// - /// 获取上传的URL - /// - /// - /// - /// - public string GetUploadUrl(string path, int expire = 7200) + ServeUrl = options.ServeUrl; + Bucket = options.Bucket; + var mcb = new MinioClient().WithEndpoint(endpoint) + .WithCredentials(options.AccessKey, options.SecretKey); + if (useSsl) { - return Mc.PresignedPutObjectAsync(new PresignedPutObjectArgs().WithBucket(Bucket) - .WithObject(path).WithExpiry(expire)).Result; + mcb = mcb.WithSSL(); } + Mc = mcb.Build(); - /// - /// 获取下载的URL - /// - /// - /// - /// - public string GetDownloadUrl(string path, int expire = 600) + var found = Mc.BucketExistsAsync(new BucketExistsArgs().WithBucket(Bucket)).Result; + if (!found) { - return Mc.PresignedGetObjectAsync(new PresignedGetObjectArgs().WithBucket(Bucket).WithObject(path) - .WithExpiry(expire)).Result; - } - - - public string UploadFile(string path, Stream stream, string contentType = "image/png") - { - Mc.PutObjectAsync(new PutObjectArgs().WithBucket(Bucket).WithObject(path).WithObjectSize(stream.Length) - .WithStreamData(stream).WithContentType(contentType)).Wait(); - return null; - } - - public string FullUrl(string path) - { - var httpRequest = HttpContextAccessor.HttpContext?.Request; - var url = $"{httpRequest?.Scheme}://{httpRequest?.Host}"; - if (string.IsNullOrEmpty(path)) - { - return path; - } - - return path.StartsWith("~") ? url + path.Substring(1, path.Length - 1) : $"{ServeUrl}{path}"; + Mc.MakeBucketAsync(new MakeBucketArgs().WithBucket(Bucket)).Wait(); } } + + /// + /// 获取上传的URL + /// + /// + /// + /// + public string GetUploadUrl(string path, int expire = 7200) + { + return Mc.PresignedPutObjectAsync(new PresignedPutObjectArgs().WithBucket(Bucket) + .WithObject(path).WithExpiry(expire)).Result; + } + + /// + /// 获取下载的URL + /// + /// + /// + /// + public string GetDownloadUrl(string path, int expire = 600) + { + return Mc.PresignedGetObjectAsync(new PresignedGetObjectArgs().WithBucket(Bucket).WithObject(path) + .WithExpiry(expire)).Result; + } + + + public string UploadFile(string path, Stream stream, string contentType = "image/png") + { + Mc.PutObjectAsync(new PutObjectArgs().WithBucket(Bucket).WithObject(path).WithObjectSize(stream.Length) + .WithStreamData(stream).WithContentType(contentType)).Wait(); + return null; + } + + public string FullUrl(string path) + { + var httpRequest = HttpContextAccessor.HttpContext?.Request; + var url = $"{httpRequest?.Scheme}://{httpRequest?.Host}"; + if (string.IsNullOrEmpty(path)) + { + return path; + } + + return path.StartsWith('~') ? string.Concat(url, path.AsSpan(1, path.Length - 1)) : $"{ServeUrl}{path}"; + } } \ No newline at end of file diff --git a/Helpers/SimApiUtil.cs b/Helpers/SimApiUtil.cs index 14023d2..2f19282 100644 --- a/Helpers/SimApiUtil.cs +++ b/Helpers/SimApiUtil.cs @@ -7,69 +7,68 @@ using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Text.Unicode; -namespace SimApi.Helpers +namespace SimApi.Helpers; + +public static class SimApiUtil { - public static class SimApiUtil + /// + /// 当前CST时间 + /// + public static DateTime CstNow => DateTime.UtcNow.AddHours(8); + + /// + /// JSON序列化常规选项 + /// + public static JsonSerializerOptions JsonOption => new() { - /// - /// 当前CST时间 - /// - public static DateTime CstNow => DateTime.UtcNow.AddHours(8); + Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; - /// - /// JSON序列化常规选项 - /// - public static JsonSerializerOptions JsonOption => new() + /// + /// 当前秒级时间戳 + /// + public static double TimestampNow => (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds; + + /// + /// 检测手机号是否正确 + /// + /// 手机号码 + /// + public static bool CheckCell(string cell) + { + var regex = new Regex("^1[3456789]\\d{9}$"); + return regex.IsMatch(cell); + } + + /// + /// MD5加密字符串 + /// + /// 源字符串 + /// 加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位 + /// + public static string Md5(string source, string mode = "x2") + { + var sor = Encoding.UTF8.GetBytes(source); + var md5 = MD5.Create(); + var result = md5.ComputeHash(sor); + var strbul = new StringBuilder(40); + foreach (var t in result) { - Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - - /// - /// 当前秒级时间戳 - /// - public static double TimestampNow => (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds; - - /// - /// 检测手机号是否正确 - /// - /// 手机号码 - /// - public static bool CheckCell(string cell) - { - var regex = new Regex("^1[3456789]\\d{9}$"); - return regex.IsMatch(cell); + strbul.Append(t.ToString(mode)); } - /// - /// MD5加密字符串 - /// - /// 源字符串 - /// 加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位 - /// - public static string Md5(string source, string mode = "x2") - { - var sor = Encoding.UTF8.GetBytes(source); - var md5 = MD5.Create(); - var result = md5.ComputeHash(sor); - var strbul = new StringBuilder(40); - foreach (var t in result) - { - strbul.Append(t.ToString(mode)); - } + return strbul.ToString(); + } - return strbul.ToString(); - } - - /// - /// 将对象序列化成JSON (控制台输出中文不会被编码) - /// - /// - /// - public static string Json(object obj) - { - return JsonSerializer.Serialize(obj, JsonOption); - } + /// + /// 将对象序列化成JSON (控制台输出中文不会被编码) + /// + /// + /// + public static string Json(object obj) + { + return JsonSerializer.Serialize(obj, JsonOption); } } \ No newline at end of file diff --git a/Logger/SimApiLogger.cs b/Logger/SimApiLogger.cs index 1fd4c53..a663791 100644 --- a/Logger/SimApiLogger.cs +++ b/Logger/SimApiLogger.cs @@ -2,40 +2,32 @@ using System; using Microsoft.Extensions.Logging; using SimApi.Helpers; -namespace SimApi.Logger +namespace SimApi.Logger; + +public class SimApiLogger(string name) : ILogger { - public class SimApiLogger : ILogger + public IDisposable BeginScope(TState state) => default!; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, + Func formatter) { - private string Name { get; } - - public SimApiLogger(string name) + Console.ForegroundColor = logLevel switch { - Name = name; - } - - public IDisposable BeginScope(TState state) => default!; - - public bool IsEnabled(LogLevel logLevel) => true; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, - Func formatter) + LogLevel.Debug => ConsoleColor.DarkMagenta, + LogLevel.Information => ConsoleColor.DarkCyan, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Error => ConsoleColor.Red, + LogLevel.Critical => ConsoleColor.DarkRed, + _ => ConsoleColor.White + }; + var message = + $"[ {name} ][ {SimApiUtil.CstNow.ToString("yyyy-MM-dd HH:mm:ss:ffff")} ][ {logLevel.ToString()} ]\n{state}\n"; + if (exception != null) { - Console.ForegroundColor = logLevel switch - { - LogLevel.Debug => ConsoleColor.DarkMagenta, - LogLevel.Information => ConsoleColor.DarkCyan, - LogLevel.Warning => ConsoleColor.Yellow, - LogLevel.Error => ConsoleColor.Red, - LogLevel.Critical => ConsoleColor.DarkRed, - _ => ConsoleColor.White - }; - var message = - $"[ {Name} ][ {SimApiUtil.CstNow.ToString("yyyy-MM-dd HH:mm:ss:ffff")} ][ {logLevel.ToString()} ]\n{state}\n"; - if (exception != null) - { - message += $"{exception}\n"; - } - Console.WriteLine(message); + message += $"{exception}\n"; } + Console.WriteLine(message); } } \ No newline at end of file diff --git a/Logger/SimApiLoggerProvider.cs b/Logger/SimApiLoggerProvider.cs index 48b0a7d..3225ea2 100644 --- a/Logger/SimApiLoggerProvider.cs +++ b/Logger/SimApiLoggerProvider.cs @@ -1,22 +1,20 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Logging; -namespace SimApi.Logger +namespace SimApi.Logger; + +public class SimApiLoggerProvider : ILoggerProvider { - public class SimApiLoggerProvider : ILoggerProvider + private readonly ConcurrentDictionary _loggers = new(); + + + public ILogger CreateLogger(string categoryName) { - private readonly ConcurrentDictionary _loggers = - new ConcurrentDictionary(); + return _loggers.GetOrAdd(categoryName, name => new SimApiLogger(name)); + } - - public ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, name => new SimApiLogger(name)); - } - - public void Dispose() - { - _loggers.Clear(); - } + public void Dispose() + { + _loggers.Clear(); } } \ No newline at end of file diff --git a/Middlewares/SimApiAuthMiddleware.cs b/Middlewares/SimApiAuthMiddleware.cs index 409db51..8bf4cc0 100644 --- a/Middlewares/SimApiAuthMiddleware.cs +++ b/Middlewares/SimApiAuthMiddleware.cs @@ -4,38 +4,30 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Distributed; using SimApi.Communications; -namespace SimApi.Middlewares +namespace SimApi.Middlewares; + +/// +/// 认证信息获取中间件 +/// +public class SimApiAuthMiddleware(RequestDelegate next) { - /// - /// 认证信息获取中间件 - /// - public class SimApiAuthMiddleware + public Task Invoke(HttpContext httpContext, IDistributedCache cache) { - private RequestDelegate Next { get; } - - public SimApiAuthMiddleware(RequestDelegate next) + string token = null; + if (httpContext.Request.Headers.TryGetValue("Token", out var header)) { - Next = next; + token = header; } - public Task Invoke(HttpContext httpContext, IDistributedCache cache) + if (!string.IsNullOrEmpty(token)) { - string token = null; - if (httpContext.Request.Headers.ContainsKey("Token")) + var login = cache.GetString(token); + if (login != null) { - token = httpContext.Request.Headers["Token"]; + httpContext.Items.Add("LoginInfo", JsonSerializer.Deserialize(login)); } - - if (!string.IsNullOrEmpty(token)) - { - var login = cache.GetString(token); - if (login != null) - { - httpContext.Items.Add("LoginInfo", JsonSerializer.Deserialize(login)); - } - } - - return Next(httpContext); } + + return next(httpContext); } -} +} \ No newline at end of file diff --git a/Middlewares/SimApiExceptionMiddleware.cs b/Middlewares/SimApiExceptionMiddleware.cs index 75f8695..10b3cb6 100644 --- a/Middlewares/SimApiExceptionMiddleware.cs +++ b/Middlewares/SimApiExceptionMiddleware.cs @@ -6,75 +6,61 @@ using SimApi.Communications; using Microsoft.Extensions.Logging; using SimApi.Exceptions; -namespace SimApi.Middlewares +namespace SimApi.Middlewares; + +/// +/// 异常处理中间件 +/// +public class SimApiExceptionMiddleware(RequestDelegate next, ILogger log) { - /// - /// 异常处理中间件 - /// - public class SimApiExceptionMiddleware + public async Task InvokeAsync(HttpContext context) { - private RequestDelegate Next { get; } - - private ILogger Log { get; } - - public SimApiExceptionMiddleware(RequestDelegate next, ILogger log) + if (context.Request.Headers.TryGetValue("Query-Id", out var header)) { - Log = log; - Next = next; + context.Response.Headers["Query-Id"] = header; } - public async Task InvokeAsync(HttpContext context) + SimApiBaseResponse response; + try { - if (context.Request.Headers.ContainsKey("Query-Id")) + await next(context); + if (context.Response.StatusCode != 200) { - context.Response.Headers["Query-Id"] = context.Request.Headers["Query-Id"]; - } - - var response = new SimApiBaseResponse(); - try - { - await Next(context); - if (context.Response.StatusCode != 200) + if (!new[] { 301, 302 }.Contains(context.Response.StatusCode)) { - if (!new[] - { - 301, 302 - }.Contains(context.Response.StatusCode)) - { - throw new SimApiException(context.Response.StatusCode); - } + throw new SimApiException(context.Response.StatusCode); } } - catch (SimApiException ex) - { - response = string.IsNullOrEmpty(ex.Message) - ? new SimApiBaseResponse(ex.Code) - : new SimApiBaseResponse(ex.Code, ex.Message); - - ErrorResponse(context, response); - } - catch (Exception ex) - { - Log.LogError(ex.Message); - Log.LogError(ex.StackTrace); - response = new SimApiBaseResponse(500, ex.Message); - ErrorResponse(context, response); - } } - - /// - /// 异常抛出错误 - /// - /// - /// - private void ErrorResponse(HttpContext context, SimApiBaseResponse response) + catch (SimApiException ex) { - if (!context.Response.HasStarted) - { - context.Response.StatusCode = 200; - context.Response.Headers.Add("Content-Type", "application/json"); - context.Response.WriteAsync(response.ToString()); - } + response = string.IsNullOrEmpty(ex.Message) + ? new SimApiBaseResponse(ex.Code) + : new SimApiBaseResponse(ex.Code, ex.Message); + + ErrorResponse(context, response); + } + catch (Exception ex) + { + log.LogError(ex.Message); + log.LogError(ex.StackTrace); + response = new SimApiBaseResponse(500, ex.Message); + ErrorResponse(context, response); + } + } + + /// + /// 异常抛出错误 + /// + /// + /// + private void ErrorResponse(HttpContext context, SimApiBaseResponse response) + { + if (!context.Response.HasStarted) + { + context.Response.StatusCode = 200; + context.Response.Headers.Append("Content-Type", "application/json"); + context.Response.WriteAsync(response.ToString()); } } } \ No newline at end of file diff --git a/Models/SimApiBaseModel.cs b/Models/SimApiBaseModel.cs index 790491e..6c42a57 100644 --- a/Models/SimApiBaseModel.cs +++ b/Models/SimApiBaseModel.cs @@ -1,91 +1,89 @@ using System; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using System.Text.Json; using SimApi.Helpers; -namespace SimApi.Models +namespace SimApi.Models; + +public class SimApiBaseModel { - public class SimApiBaseModel + [Column(Order = 1)] + public string Id { get; set; } = Guid.NewGuid().ToString(); + + [Column(Order = 9998)] + public DateTime UpdatedAt { get; set; } = SimApiUtil.CstNow; + + [Column(Order = 9999)] + public DateTime CreatedAt { get; set; } = SimApiUtil.CstNow; + + protected virtual string[] MapperIgnoreField { get; set; } = { "Id", "CreatedAt", "UpdatedAt" }; + + protected virtual string UpdatedTimeField { get; set; } = "UpdatedAt"; + + public void MapData(TS source, bool mapAll = false) { - [Column(Order = 1)] - public string Id { get; set; } = Guid.NewGuid().ToString(); - - [Column(Order = 9998)] - public DateTime UpdatedAt { get; set; } = SimApiUtil.CstNow; - - [Column(Order = 9999)] - public DateTime CreatedAt { get; set; } = SimApiUtil.CstNow; - - protected virtual string[] MapperIgnoreField { get; set; } = { "Id", "CreatedAt", "UpdatedAt" }; - - protected virtual string UpdatedTimeField { get; set; } = "UpdatedAt"; - - public void MapData(TS source, bool mapAll = false) - { - //获取要赋值的源数据不为null的项目 - var sourceProps = source.GetType().GetProperties().Where(x => x.GetValue(source) != null) - .Select(x => new - { - x.Name, - x.PropertyType - }) - .ToDictionary(x => x.Name, x => x.PropertyType); - - //获取目标对象的属性信息 - var targetProps = GetType().GetProperties().Select(x => new - { - x.Name, - x.PropertyType - }) - .ToDictionary(x => x.Name, x => x.PropertyType); - foreach (var sp in sourceProps.Where(sp => - targetProps.ContainsKey(sp.Key) && sp.Value == targetProps[sp.Key] && - (!MapperIgnoreField.Contains(sp.Key) || mapAll))) + //获取要赋值的源数据不为null的项目 + var sourceProps = source.GetType().GetProperties().Where(x => x.GetValue(source) != null) + .Select(x => new { - GetType().GetProperty(sp.Key)? - .SetValue(this, source.GetType().GetProperty(sp.Key)?.GetValue(source)); - } + x.Name, + x.PropertyType + }) + .ToDictionary(x => x.Name, x => x.PropertyType); - UpdateTime(); - } - - public void MapData(TS source, string[] mapFields) - { - //获取要赋值的源数据不为null的项目 - var sourceProps = source.GetType().GetProperties().Where(x => x.GetValue(source) != null) - .Select(x => new - { - x.Name, - x.PropertyType - }) - .ToDictionary(x => x.Name, x => x.PropertyType); - - //获取目标对象的属性信息 - var targetProps = GetType().GetProperties().Select(x => new - { - x.Name, - x.PropertyType - }) - .ToDictionary(x => x.Name, x => x.PropertyType); - foreach (var sp in sourceProps.Where(sp => - targetProps.ContainsKey(sp.Key) && sp.Value == targetProps[sp.Key] && - mapFields.Contains(sp.Key))) + //获取目标对象的属性信息 + var targetProps = GetType().GetProperties().Select(x => new { - GetType().GetProperty(sp.Key)? - .SetValue(this, source.GetType().GetProperty(sp.Key)?.GetValue(source)); - } - - UpdateTime(); - } - - /// - /// 更新update时间 - /// - /// - public void UpdateTime() + x.Name, + x.PropertyType + }) + .ToDictionary(x => x.Name, x => x.PropertyType); + foreach (var sp in sourceProps.Where(sp => + targetProps.ContainsKey(sp.Key) && sp.Value == targetProps[sp.Key] && + (!MapperIgnoreField.Contains(sp.Key) || mapAll))) { - GetType().GetProperty(UpdatedTimeField)?.SetValue(this, SimApiUtil.CstNow); + GetType().GetProperty(sp.Key)? + .SetValue(this, source.GetType().GetProperty(sp.Key)?.GetValue(source)); } + + UpdateTime(); + } + + public void MapData(TS source, string[] mapFields) + { + //获取要赋值的源数据不为null的项目 + var sourceProps = source.GetType().GetProperties().Where(x => x.GetValue(source) != null) + .Select(x => new + { + x.Name, + x.PropertyType + }) + .ToDictionary(x => x.Name, x => x.PropertyType); + + //获取目标对象的属性信息 + var targetProps = GetType().GetProperties().Select(x => new + { + x.Name, + x.PropertyType + }) + .ToDictionary(x => x.Name, x => x.PropertyType); + foreach (var sp in sourceProps.Where(sp => + targetProps.ContainsKey(sp.Key) && sp.Value == targetProps[sp.Key] && + mapFields.Contains(sp.Key))) + { + GetType().GetProperty(sp.Key)? + .SetValue(this, source.GetType().GetProperty(sp.Key)?.GetValue(source)); + } + + UpdateTime(); + } + + /// + /// 更新update时间 + /// + /// + public void UpdateTime() + { + GetType().GetProperty(UpdatedTimeField)?.SetValue(this, SimApiUtil.CstNow); } } \ No newline at end of file diff --git a/SimApi.csproj b/SimApi.csproj index 1c593b9..9853b60 100644 --- a/SimApi.csproj +++ b/SimApi.csproj @@ -1,42 +1,42 @@ - - Library - true - 0.2.3 - xRain@SimcuTeam - AspNetCore一个方便的API文档,捕获异常,统一输入输出的API类库 - Simcu.SimApi - true - snupkg - true - 5.0.0 - false - true - 5.0.2 - net7.0;net8.0 - + + Library + true + 0.2.3 + xRain@SimcuTeam + AspNetCore一个方便的API文档,捕获异常,统一输入输出的API类库 + Simcu.SimApi + true + snupkg + true + 5.0.0 + false + true + 5.0.2 + net8.0 + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/SimApiExtensions.cs b/SimApiExtensions.cs index ce2e3da..e4b62f5 100644 --- a/SimApiExtensions.cs +++ b/SimApiExtensions.cs @@ -9,257 +9,256 @@ using Microsoft.Extensions.Logging; using SimApi.Configs; using SimApi.Logger; -namespace SimApi +namespace SimApi; + +/// +/// 加入系统的扩展信息 +/// +public static class SimApiExtensions { - /// - /// 加入系统的扩展信息 - /// - public static class SimApiExtensions + //**********快捷添加************** + public static IServiceCollection AddSimApi(this IServiceCollection builder, + Action options = null) { - //**********快捷添加************** - public static IServiceCollection AddSimApi(this IServiceCollection builder, - Action options = null) + var simApiOptions = new SimApiOptions(); + options?.Invoke(simApiOptions); + if (simApiOptions.EnableLogger) { - var simApiOptions = new SimApiOptions(); - options?.Invoke(simApiOptions); - if (simApiOptions.EnableLogger) + builder.AddLogging(logger => { - builder.AddLogging(logger => + logger.ClearProviders(); + logger.AddProvider(new SimApiLoggerProvider()); + }); + } + // 是否使用 AUTH + if (simApiOptions.EnableSimApiAuth) + { + builder.AddScoped(); + } + + if (simApiOptions.EnableCors) + { + builder.AddCors(cors => cors.AddPolicy("any", + policy => { policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin(); })); + } + + if (simApiOptions.EnableSynapse) + { + builder.AddSingleton(); + } + + // 使用SimApiDoc + if (simApiOptions.EnableSimApiDoc) + { + var docOptions = simApiOptions.SimApiDocOptions; + builder.AddSwaggerGen(x => + { + foreach (var group in docOptions.ApiGroups) { - logger.ClearProviders(); - logger.AddProvider(new SimApiLoggerProvider()); - }); - } - // 是否使用 AUTH - if (simApiOptions.EnableSimApiAuth) - { - builder.AddScoped(); - } + x.SwaggerDoc(group.Id, new OpenApiInfo + { + Title = group.Name, + Description = group.Description + }); + } - if (simApiOptions.EnableCors) - { - builder.AddCors(cors => cors.AddPolicy("any", - policy => { policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin(); })); - } - - if (simApiOptions.EnableSynapse) - { - builder.AddSingleton(); - } - - // 使用SimApiDoc - if (simApiOptions.EnableSimApiDoc) - { - var docOptions = simApiOptions.SimApiDocOptions; - builder.AddSwaggerGen(x => + x.EnableAnnotations(); + var haveOauth = false; + var haveSimApiAuth = false; + var oauthFlows = new OpenApiOAuthFlows(); + foreach (var auth in docOptions.ApiAuth.Type) { - foreach (var group in docOptions.ApiGroups) + switch (auth) { - x.SwaggerDoc(group.Id, new OpenApiInfo - { - Title = group.Name, - Description = group.Description - }); - } - - x.EnableAnnotations(); - var haveOauth = false; - var haveSimApiAuth = false; - var oauthFlows = new OpenApiOAuthFlows(); - foreach (var auth in docOptions.ApiAuth.Type) - { - switch (auth) - { - case "SimApiAuth": - x.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "HeaderToken" - } - }, - new[] { "readAccess", "writeAccess" } - } - }); - haveSimApiAuth = true; - break; - case "ClientCredentials": - oauthFlows.ClientCredentials = new OpenApiOAuthFlow - { - TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), - Scopes = docOptions.ApiAuth.Scopes - }; - haveOauth = true; - break; - case "Implicit": - oauthFlows.Implicit = new OpenApiOAuthFlow - { - AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl, - UriKind.RelativeOrAbsolute), - Scopes = docOptions.ApiAuth.Scopes - }; - haveOauth = true; - break; - case "AuthorizationCode": - oauthFlows.AuthorizationCode = new OpenApiOAuthFlow - { - TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), - AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl, - UriKind.RelativeOrAbsolute), - Scopes = docOptions.ApiAuth.Scopes - }; - haveOauth = true; - break; - case "Password": - oauthFlows.Password = new OpenApiOAuthFlow - { - TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), - Scopes = docOptions.ApiAuth.Scopes - }; - haveOauth = true; - break; - } - } - - if (haveOauth) - { - x.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme - { - Type = SecuritySchemeType.OAuth2, - Flows = oauthFlows, - Description = docOptions.ApiAuth.Description, - In = ParameterLocation.Header - }); - x.AddSecurityRequirement(new OpenApiSecurityRequirement - { + case "SimApiAuth": + x.AddSecurityRequirement(new OpenApiSecurityRequirement { - new OpenApiSecurityScheme { - Reference = new OpenApiReference + new OpenApiSecurityScheme { - Type = ReferenceType.SecurityScheme, - Id = "oauth2" - } - }, - new[] { "SimApiAuth" } - } - }); + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "HeaderToken" + } + }, + new[] { "readAccess", "writeAccess" } + } + }); + haveSimApiAuth = true; + break; + case "ClientCredentials": + oauthFlows.ClientCredentials = new OpenApiOAuthFlow + { + TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), + Scopes = docOptions.ApiAuth.Scopes + }; + haveOauth = true; + break; + case "Implicit": + oauthFlows.Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl, + UriKind.RelativeOrAbsolute), + Scopes = docOptions.ApiAuth.Scopes + }; + haveOauth = true; + break; + case "AuthorizationCode": + oauthFlows.AuthorizationCode = new OpenApiOAuthFlow + { + TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), + AuthorizationUrl = new Uri(docOptions.ApiAuth.AuthorizationUrl, + UriKind.RelativeOrAbsolute), + Scopes = docOptions.ApiAuth.Scopes + }; + haveOauth = true; + break; + case "Password": + oauthFlows.Password = new OpenApiOAuthFlow + { + TokenUrl = new Uri(docOptions.ApiAuth.TokenUrl, UriKind.RelativeOrAbsolute), + Scopes = docOptions.ApiAuth.Scopes + }; + haveOauth = true; + break; } + } - if (haveSimApiAuth) + if (haveOauth) + { + x.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { - x.AddSecurityDefinition("HeaderToken", + Type = SecuritySchemeType.OAuth2, + Flows = oauthFlows, + Description = docOptions.ApiAuth.Description, + In = ParameterLocation.Header + }); + x.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { new OpenApiSecurityScheme { - Name = "Token", - In = ParameterLocation.Header - }); - } - }); - } + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "oauth2" + } + }, + new[] { "SimApiAuth" } + } + }); + } - // 使用Header转发,应对代理后获取真实ip - if (simApiOptions.EnableForwardHeaders) - { - builder.Configure(fwOptions => + if (haveSimApiAuth) { - fwOptions.ForwardedHeaders = ForwardedHeaders.All; - fwOptions.KnownNetworks.Clear(); - fwOptions.KnownProxies.Clear(); - }); - } - - if (simApiOptions.EnableLowerUrl) - { - builder.AddRouting(rOptions => rOptions.LowercaseUrls = true); - } - - if (simApiOptions.EnableSimApiStorage) - { - builder.AddHttpContextAccessor(); - builder.AddSingleton(); - } - - builder.AddSingleton(simApiOptions); - return builder; + x.AddSecurityDefinition("HeaderToken", + new OpenApiSecurityScheme + { + Name = "Token", + In = ParameterLocation.Header + }); + } + }); } - /// - /// 使用所有SimApi自定义中间件 - /// - /// - /// - public static IApplicationBuilder UseSimApi(this IApplicationBuilder builder) + // 使用Header转发,应对代理后获取真实ip + if (simApiOptions.EnableForwardHeaders) { - var options = builder.ApplicationServices.GetRequiredService(); - - var logger = builder.ApplicationServices.GetRequiredService>(); - - if (options.EnableForwardHeaders) + builder.Configure(fwOptions => { - logger.LogInformation("开始配置ForwardedHeaders..."); - builder.UseForwardedHeaders(); - } - - if (options.EnableCors) - { - logger.LogInformation("开始配置Cors全部允许..."); - builder.UseCors("any"); - } - - if (options.EnableSimApiAuth) - { - logger.LogInformation("开始配置SimApiAuth..."); - builder.UseMiddleware(); - } - - if (options.EnableSimApiDoc) - { - logger.LogInformation("开始配置SimApiDoc..."); - var docOptions = options.SimApiDocOptions; - builder.UseSwagger(x => x.RouteTemplate = "/swagger/{documentName}.json").UseSwaggerUI(x => - { - x.DocumentTitle = docOptions.DocumentTitle; - foreach (var group in docOptions.ApiGroups) - { - x.SwaggerEndpoint($"/swagger/{group.Id}.json", name: group.Name); - } - - x.EnableValidator(); - x.SupportedSubmitMethods(docOptions.SupportedMethod); - x.DisplayRequestDuration(); - }); - } - - if (options.EnableSimApiException) - { - logger.LogInformation("开始配置SimApiException..."); - builder.UseMiddleware(); - } - - //请求一下检测存储错误 - if (options.EnableSimApiStorage) - { - logger.LogInformation("开始配置SimApiStorage..."); - builder.ApplicationServices.GetService(); - } - - if (options.EnableLowerUrl) - { - logger.LogInformation("开始配置使用URL小写..."); - } - - if (options.EnableSynapse) - { - var synapse = builder.ApplicationServices.GetRequiredService(); - synapse.Init(); - } - - return builder; + fwOptions.ForwardedHeaders = ForwardedHeaders.All; + fwOptions.KnownNetworks.Clear(); + fwOptions.KnownProxies.Clear(); + }); } + + if (simApiOptions.EnableLowerUrl) + { + builder.AddRouting(rOptions => rOptions.LowercaseUrls = true); + } + + if (simApiOptions.EnableSimApiStorage) + { + builder.AddHttpContextAccessor(); + builder.AddSingleton(); + } + + builder.AddSingleton(simApiOptions); + return builder; + } + + /// + /// 使用所有SimApi自定义中间件 + /// + /// + /// + public static IApplicationBuilder UseSimApi(this IApplicationBuilder builder) + { + var options = builder.ApplicationServices.GetRequiredService(); + + var logger = builder.ApplicationServices.GetRequiredService>(); + + if (options.EnableForwardHeaders) + { + logger.LogInformation("开始配置ForwardedHeaders..."); + builder.UseForwardedHeaders(); + } + + if (options.EnableCors) + { + logger.LogInformation("开始配置Cors全部允许..."); + builder.UseCors("any"); + } + + if (options.EnableSimApiAuth) + { + logger.LogInformation("开始配置SimApiAuth..."); + builder.UseMiddleware(); + } + + if (options.EnableSimApiDoc) + { + logger.LogInformation("开始配置SimApiDoc..."); + var docOptions = options.SimApiDocOptions; + builder.UseSwagger(x => x.RouteTemplate = "/swagger/{documentName}.json").UseSwaggerUI(x => + { + x.DocumentTitle = docOptions.DocumentTitle; + foreach (var group in docOptions.ApiGroups) + { + x.SwaggerEndpoint($"/swagger/{group.Id}.json", name: group.Name); + } + + x.EnableValidator(); + x.SupportedSubmitMethods(docOptions.SupportedMethod); + x.DisplayRequestDuration(); + }); + } + + if (options.EnableSimApiException) + { + logger.LogInformation("开始配置SimApiException..."); + builder.UseMiddleware(); + } + + //请求一下检测存储错误 + if (options.EnableSimApiStorage) + { + logger.LogInformation("开始配置SimApiStorage..."); + builder.ApplicationServices.GetService(); + } + + if (options.EnableLowerUrl) + { + logger.LogInformation("开始配置使用URL小写..."); + } + + if (options.EnableSynapse) + { + var synapse = builder.ApplicationServices.GetRequiredService(); + synapse.Init(); + } + + return builder; } } \ No newline at end of file