最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
为ABP框架添加基础集成服务
时间:2026-09-17 10:30:01 编辑:袖梨 来源:一聚教程网
定义一个特性标记
这个标记用于标记一个枚举代表的信息。
在AbpBase.Domain.Shared项目,创建Attributes目录,然后创建一个SchemeNameAttribute类,其内容如下:
/// <summary>/// 标记枚举代表的信息/// </summary>[AttributeUsage(AttributeTargets.Field)]public class SchemeNameAttribute : Attribute{public string Message { get; set; }public SchemeNameAttribute(string message){Message = message;}}
全局统一消息格式
为了使得 Web 应用统一响应格式以及方便编写 API 时有一个统一的标准,我们需要定义一个合适的模板。
在AbpBase.Domain.Shared创建一个Apis目录。
Http 状态码
为了适配各种 HTTP 请求的响应状态,我们定义一个识别状态码的枚举。
在Apis目录,创建一个HttpStateCode.cs文件,其内容如下:
namespace AbpBase.Domain.Shared.Apis{/// <summary>/// 标准 HTTP 状态码/// <para>文档地址<inheritdoc cref="https://www.r*un*o*ob.com/http/http-status-codes.html"/></para>/// </summary>public enum HttpStateCode{Status412PreconditionFailed = 412,Status413PayloadTooLarge = 413,Status413RequestEntityTooLarge = 413,Status414RequestUriTooLong = 414,Status414UriTooLong = 414,Status415UnsupportedMediaType = 415,Status416RangeNotSatisfiable = 416,Status416RequestedRangeNotSatisfiable = 416,Status417ExpectationFailed = 417,Status418ImATeapot = 418,Status419AuthenticationTimeout = 419,Status421MisdirectedRequest = 421,Status422UnprocessableEntity = 422,Status423Locked = 423,Status424FailedDependency = 424,Status426UpgradeRequired = 426,Status428PreconditionRequired = 428,Status429TooManyRequests = 429,Status431RequestHeaderFieldsTooLarge = 431,Status451UnavailableForLegalReasons = 451,Status500InternalServerError = 500,Status501NotImplemented = 501,Status502BadGateway = 502,Status503ServiceUnavailable = 503,Status504GatewayTimeout = 504,Status505HttpVersionNotsupported = 505,Status506VariantAlsoNegotiates = 506,Status507InsufficientStorage = 507,Status508LoopDetected = 508,Status411LengthRequired = 411,Status510NotExtended = 510,Status410Gone = 410,Status408RequestTimeout = 408,Status101SwitchingProtocols = 101,Status102Processing = 102,Status200OK = 200,Status201Created = 201,Status202Accepted = 202,Status203NonAuthoritative = 203,Status204NoContent = 204,Status205ResetContent = 205,Status206PartialContent = 206,Status207MultiStatus = 207,Status208AlreadyReported = 208,Status226IMUsed = 226,Status300MultipleChoices = 300,Status301MovedPermanently = 301,Status302Found = 302,Status303SeeOther = 303,Status304NotModified = 304,Status305UseProxy = 305,Status306SwitchProxy = 306,Status307TemporaryRedirect = 307,Status308PermanentRedirect = 308,Status400BadRequest = 400,Status401Unauthorized = 401,Status402PaymentRequired = 402,Status403Forbidden = 403,Status404NotFound = 404,Status405MethodNotAllowed = 405,Status406NotAcceptable = 406,Status407ProxyAuthenticationRequired = 407,Status409Conflict = 409,Status511NetworkAuthenticationRequired = 511}}
常用的请求结果
在相同目录,创建一个CommonResponseType枚举,其内容如下:
/// <summary>/// 常用的 API 响应信息/// </summary>public enum CommonResponseType{[SchemeName("")] Default = 0,[SchemeName("请求成功")] RequstSuccess = 1,[SchemeName("请求失败")] RequstFail = 2,[SchemeName("创建资源成功")] CreateSuccess = 4,[SchemeName("创建资源失败")] CreateFail = 8,[SchemeName("更新资源成功")] UpdateSuccess = 16,[SchemeName("更新资源失败")] UpdateFail = 32,[SchemeName("删除资源成功")] DeleteSuccess = 64,[SchemeName("删除资源失败")] DeleteFail = 128,[SchemeName("请求的数据未能通过验证")] BadRequest = 256,[SchemeName("服务器出现严重错误")] Status500InternalServerError = 512}
响应模型
在Apis目录,创建一个ApiResponseModel`.cs泛型类文件,其内容如下:
namespace AbpBase.Domain.Shared.Apis{/// <summary>/// API 响应格式/// <para>避免滥用,此类不能实例化,只能通过预定义的静态方法生成</para>/// </summary>/// <typeparam name="TData"></typeparam>public abstract class ApiResponseModel<TData>{public HttpStateCode StatuCode { get; set; }public string Message { get; set; }public TData Data { get; set; }/// <summary>/// 私有类/// </summary>/// <typeparam name="TResult"></typeparam>private class PrivateApiResponseModel<TResult> : ApiResponseModel<TResult> { }}}
StatuCode:用于说明此次响应的状态;
Message:响应的信息;
Data:响应的数据;
可能你会觉得这样很奇怪,先不要问,也不要猜,照着做,后面我会告诉你为什么这样写。
然后再创建一个类:
using AbpBase.Domain.Shared.Helpers;using System;namespace AbpBase.Domain.Shared.Apis{/// <summary>/// Web 响应格式/// <para>避免滥用,此类不能实例化,只能通过预定义的静态方法生成</para>/// </summary>public abstract class ApiResponseModel : ApiResponseModel<dynamic>{/// <summary>/// 根据枚举创建响应格式/// </summary>/// <typeparam name="TEnum"></typeparam>/// <param name="code"></param>/// <param name="enumType"></param>/// <returns></returns>public static ApiResponseModel Create<TEnum>(HttpStateCode code, TEnum enumType) where TEnum : Enum{return new PrivateApiResponseModel{StatuCode = code,Message = SchemeHelper.Get(enumType),};}/// <summary>/// 创建标准的响应/// </summary>/// <typeparam name="TEnum"></typeparam>/// <typeparam name="TData"></typeparam>/// <param name="code"></param>/// <param name="enumType"></param>/// <param name="Data"></param>/// <returns></returns>public static ApiResponseModel Create<TEnum>(HttpStateCode code, TEnum enumType, dynamic Data){return new PrivateApiResponseModel{StatuCode = code,Message = SchemeHelper.Get(enumType),Data = Data};}/// <summary>/// 请求成功/// </summary>/// <param name="code"></param>/// <param name="Data"></param>/// <returns></returns>public static ApiResponseModel CreateSuccess(HttpStateCode code, dynamic Data){return new PrivateApiResponseModel{StatuCode = code,Message = "Success",Data = Data};}/// <summary>/// 私有类/// </summary>private class PrivateApiResponseModel : ApiResponseModel { }}}
同时在项目中创建一个 Helpers 文件夹,再创建一个SchemeHelper类,其内容如下:
using AbpBase.Domain.Shared.Attributes;using System;using System.Linq;using System.Reflection;namespace AbpBase.Domain.Shared.Helpers{/// <summary>/// 获取各种枚举代表的信息/// </summary>public static class SchemeHelper{private static readonly PropertyInfo SchemeNameAttributeMessage = typeof(SchemeNameAttribute).GetProperty(nameof(SchemeNameAttribute.Message));/// <summary>/// 获取一个使用了 SchemeNameAttribute 特性的 Message 属性值/// </summary>/// <typeparam name="T"></typeparam>/// <param name="type"></param>/// <returns></returns>public static string Get<T>(T type){return GetValue(type);}private static string GetValue<T>(T type){var attr = typeof(T).GetField(Enum.GetName(type.GetType(), type)).GetCustomAttributes().FirstOrDefault(x => x.GetType() == typeof(SchemeNameAttribute));if (attr == null)return string.Empty;var value = (string)SchemeNameAttributeMessage.GetValue(attr);return value;}}}
上面的类到底是干嘛的,你先不要问。
全局异常拦截器
在AbpBase.Web项目中,新建一个Filters文件夹,添加一个WebGlobalExceptionFilter.cs文件,其文件内容如下:
using AbpBase.Domain.Shared.Apis;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.Filters;using Newtonsoft.Json;using System.Threading.Tasks;namespace ApbBase.HttpApi.Filters{/// <summary>/// Web 全局异常过滤器,处理 Web 中出现的、运行时未处理的异常/// </summary>public class WebGlobalExceptionFilter : IAsyncExceptionFilter{public async Task OnExceptionAsync(ExceptionContext context){if (!context.ExceptionHandled){ApiResponseModel model = ApiResponseModel.Create(HttpStateCode.Status500InternalServerError,CommonResponseType.Status500InternalServerError);context.Result = new ContentResult{Content = JsonConvert.SerializeObject(model),StatusCode = StatusCodes.Status200OK,ContentType = "application/json; charset=utf-8"};}context.ExceptionHandled = true;await Task.CompletedTask;}}}
然后 在AbpBaseWebModule模块的ConfigureServices函数中,加上:
Configure<MvcOptions>(options =>{options.Filters.Add(typeof(WebGlobalExceptionFilter));});
这里我们还没有将写入日志,后面再增加这方面的功能。
先说明一下
前面我们定义了 ApiResponseModel 和其他一些特性还有枚举,这里解释一下原因。
ApiResponseModel 是抽象类
ApiResponseModel<T>和ApiResponseModel是抽象类,是为了避免开发者使用时,直接这样用:
ApiResponseModel mode = new ApiResponseModel{Code = 500,Message = "失败",Data = xxx};
首先这个 Code 需要按照 HTTP 状态的标准来填写,我们使用 HttpStateCode 枚举来标记,代表异常时,使用Status500InternalServerError来标识。
我非常讨厌一个 Action 的一个返回,就写一次消息的。
if(... ...)return xxxx("请求数据不能为空");if(... ...)return xxxx("xxx 要大于 10");... ..
这样每个地方一个消息说明,十分不统一,也不便于修改。
直接使用一个枚举来代表消息,而不能直接写出来,这样就可以达到统一了。
使用抽象类,可以避免开发者直接 new 一个,强制要求一定的消息格式来响应。后面可以进行更多的尝试,来体会我这样设计的便利性。
跨域请求
这里我们将配置 Web 全局允许跨域请求。
在AbpBaseWebModule模块中:
添加一个静态变量
private const string AbpBaseWebCosr = "AllowSpecificOrigins";
创建一个配置函数:
/// <summary>/// 配置跨域/// </summary>/// <param name="context"></param>private void ConfigureCors(ServiceConfigurationContext context){context.Services.AddCors(options =>{options.AddPolicy(AbpBaseWebCosr,builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());});}
在ConfigureServices函数中添加:
// 跨域请求ConfigureCors(context);
在OnApplicationInitialization中添加:
app.UseCors(AbpBaseWebCosr);// 位置在 app.UseRouting(); 后面
就这样,允许全局跨域请求就完成了。
配置 API 服务
你可以使用以下模块来配置一个 API 模块服务:
Configure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(AbpBaseHttpApiModule).Assembly, opts =>{opts.RootPath = "api/1.0";});});
我们在AbpBase.HttpApi中将其本身用于创建一个 API 服务,ABP 会将继承了AbpController、ControllerBase等的类识别为 API控制器。上面的代码同时将其默认路由的前缀设置为api/1.0。
也可以不设置前缀:
Configure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(IoTCenterWebModule).Assembly);});
由于 API 模块已经在自己的ConfigureServices创建了 API 服务,因此可以不在Web模块里面编写这部分代码。当然,也可以统一在Web中定义所有的 API 模块。
统一 API 模型验证消息
创建前
首先,如果我们这样定义一个 Action:
public class TestModel{[Required]public int Id { get; set; }[MaxLength(11)]public int Iphone { get; set; }[Required][MinLength(5)]public string Message { get; set; }}[HttpPost("/T2")]public string MyWebApi2([FromBody] TestModel model){return "请求完成";}
使用以下参数请求:
{"Id": "1","Iphone": 123456789001234567890,"Message": null}
会得到以下结果:
{"errors": {"Iphone": ["JSON integer 123456789001234567890 is too large or small for an Int32. Path 'Iphone', line 3, position 35."]},"type": "https://tools.ie***tf.org/html/rfc7231#section-6.5.1","title": "One or more validation errors occurred.","status": 400,"traceId": "|af964c79-41367b2145701111."}
这样的信息阅读起来十分不友好,前端对接也会有一定的麻烦。
这个时候我们可以统一模型验证拦截器,定义一个友好的响应格式。
创建方式
在AbpBase.Web的项目 的Filters文件夹中,创建一个InvalidModelStateFilter文件,其文件内容如下:
using AbpBase.Domain.Shared.Apis;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.DependencyInjection;using System.Linq;namespace AbpBase.Web.Filters{public static class InvalidModelStateFilter{/// <summary>/// 统一模型验证/// <para>控制器必须添加 [ApiController] 才能被此过滤器拦截</para>/// </summary>/// <param name="services"></param>public static void GlabalInvalidModelStateFilter(this IServiceCollection services){services.Configure<ApiBehaviorOptions>(options =>{options.InvalidModelStateResponseFactory = actionContext =>{if (actionContext.ModelState.IsValid)return new BadRequestObjectResult(actionContext.ModelState);int count = actionContext.ModelState.Count;ValidationErrors[] errors = new ValidationErrors[count];int i = 0;foreach (var item in actionContext.ModelState){errors[i] = new ValidationErrors{Member = item.Key,Messages = item.Value.Errors?.Select(x => x.ErrorMessage).ToArray()};i++;}// 响应消息var result = ApiResponseModel.Create(HttpStateCode.Status400BadRequest, CommonResponseType.BadRequest, errors);var objectResult = new BadRequestObjectResult(result);objectResult.StatusCode = StatusCodes.Status400BadRequest;return objectResult;};});}/// <summary>/// 用于格式化实体验证信息的模型/// </summary>private class ValidationErrors{/// <summary>/// 验证失败的字段/// </summary>public string Member { get; set; }/// <summary>/// 此字段有何种错误/// </summary>public string[] Messages { get; set; }}}}
在ConfigureServices函数中,添加以下代码:
// 全局 API 请求实体验证失败信息格式化context.Services.GlabalInvalidModelStateFilter();
创建后
让我们看看增加了统一模型验证器后,同样的请求返回的消息。
请求:
{"Id": "1","Iphone": 123456789001234567890,"Message": null}
返回:
{"statuCode": 400,"message": "请求的数据未能通过验证","data": [{"member": "Iphone","messages": ["JSON integer 123456789001234567890 is too large or small for an Int32. Path 'Iphone', line 3, position 35."]}]}
说明我们的统一模型验证响应起到了作用。
但是有些验证会直接报异常而不会流转到上面的拦截器中,有些模型验证特性用错对象的话,他会报错异常的。例如上面的 MaxLength ,已经用错了,MaxLength 是指定属性中允许的数组或字符串数据的最大长度,不能用在 int 类型上。大家测试一下请求下面的 json,会发现报异常。
{"Id": 1,"Iphone": 1234567900,"Message": "nullable"}
以下是一些 ASP.NET Core 内置验证特性,大家记得别用错:
[CreditCard]:验证属性是否具有格式。 需要 JQuery 验证其他方法。[Compare]:验证模型中的两个属性是否匹配。[EmailAddress]:验证属性是否具有电子邮件格式。[Phone]:验证属性是否具有电话号码格式。[Range]:验证属性值是否在指定的范围内。[RegularExpression]:验证属性值是否与指定的正则表达式匹配。[Required]:验证字段是否不为 null。 有关此属性的行为的详细信息[StringLength]:验证字符串属性值是否不超过指定长度限制。[Url]:验证属性是否具有 URL 格式。[Remote]:通过在服务器上调用操作方法来验证客户端上的输入。[MaxLength ]MaxLength 是指定属性中允许的数组或字符串数据的最大长度参考:https://docs.m**icr*osoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1
本系列第二篇到此,接下来第三篇会继续添加一些基础服务。
补充:为什么需要统一格式
首先,你看一下这样的代码:

在每个 Action 中,都充满了这种写法,每个相同的验证问题,在每个 Action 返回的文字都不一样,没有规范可言。一个人写一个 return,就加上一下自己要表达的文字,一个项目下来,多少return?全是这种代码,不堪入目。
通过统一模型验证和统一消息返回格式,就可以避免这些情况。
源码地址:https://git*hub*.c*om/whuanle/AbpBaseStruct
本教程结果代码位置:https://git*hub*.c*om/whuanle/AbpBaseStruct/tree/master/src/2/AbpBase
到此这篇关于为ABP框架添加基础集成服务的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持一聚教程网。
相关文章
- Copilot Word实操:快速生成管理层销售项目周报 09-17
- 别只对 AI 说“我要什么”:把需求讲清楚才有好结果 09-17
- EntityFramework系统架构与原理介绍 09-17
- 岗位Agent试岗:别只验收它的回答 09-17
- 企业级智能问答系统搭建实战:向量库架构与取舍 09-17
- Spring AI Alibaba Graph 入门实践:理解黑板、节点与边 09-17