自己造轮子是一件苦差事。 现在,您可以专注于业务开发,仅需集成 ⭐️Furion⭐️ 即可。
Skip to main content

6. 规范化接口文档

6.1 什么是接口文档

在现在移动为王、多端互辅、前端百花齐放的开放时代,不再是一人包揽式开发,大家各司其职,后端工程师负责接口开发,前端负责接口联调,也就是互联网现在流行的前后端分离架构。

所以就需要由前后端工程师共同定义接口,编写接口文档,之后大家按照这个接口文档进行开发、维护及开放给第三方。

6.2 为什么要写接口文档

  • 能够让前端开发与后台开发人员更好的配合,提高工作效率
  • 项目迭代或者项目人员更迭时,方便后期人员查看和维护
  • 方便测试人员进行接口测试

6.3 为什么需要规范化文档

由于每个公司后端人员技术参差不齐,技术文档能力也不例外,导致接口定义及文档五花八门,不同项目或不同公司对接极其困难,而且体验糟糕。所以,无规矩不成方圆,为了开发人员间更好的配合,迫切需要整理出一套规范。

通常接口规范分为六个部分:

6.3.1 协议规范

为了确保不同系统/模块间的数据交互,需要事先约定好通讯协议,如:TCP、HTTP、HTTPS 协议。为了确保数据交互安全,建议使用 HTTPS 协议

6.3.2 接口路径规范

作为接口路径,为了方便清晰的区分来自不同的系统,可以采用不同系统/模块名作为接口路径前缀,如:支付模块:/pay/xxx,订单模块:/order/xxx

6.3.3 版本控制规范

为了便于后期接口的升级和维护,建议在接口路径中加入版本号,便于管理,实现接口多版本的可维护性。如:接口路径中添加类似"v1"、"v2"等版本号

6.3.4 接口命名规范

和 C# 命名规范一样,好的、统一的接口命名规范,不仅可以增强其可读性,而且还会减少很多不必要的口头/书面上的解释。可使用"驼峰命名法"按照实现接口的业务类型、业务场景等命名,有必要时可采取多级目录命名,但目录不宜过长,两级目录较为适宜

  • 常见命名方式:
    • 接口名称动词前/后缀化: 接口名称以接口数据操作的动词为前/后缀,常见动词有:Add、Delete、Update、Query、Get、Send、Save、Detail、List等,如:新建用户 AddUser、查询订单详情 QueryOrderDetail
    • 接口名称动词 + 请求方式:接口路径中包含具体接口名称的名词,接口数据操作动作以 HTTP 请求方式来区分。常用的 HTTP 请求方式有:
      • GET:从服务器取出资源(一项或多项)
      • POST:在服务器新建一个资源
      • PUT:在服务器更新资源(客户端提供改变后的完整资源)
      • PATCH:在服务器更新资源(客户端提供改变的属性)
      • DELETE:从服务器删除资源

6.3.5 请求参数规范

  • 请求方式:按照 GET、POST、PUT 等含义定义,避免出现不一致现象,对人造成误解、歧义
    • 请求头:请求头根据项目需求添加配置参数。如:请求数据格式,accept=application/json 等。如有需要,请求头可根据项目需求要求传入用户 token、唯一验签码等加密数据
    • 请求参数/请求体: 请求参数字段,尽可能与数据库表字段、对象属性名等保持一致,因为保持一致是最省事,最舒服的一件事

6.3.6 返回数据规范

统一规范返回数据的格式,对己对彼都有好处,此处以 json 格式为例。返回数据应包含:返回状态码、返回状态信息、具体数据返回数据中的状态码、状态信息,常指具体的业务状态,不建议和 HTTP 状态码混在一起。HTTP 状态,是用来体现 HTTP 链路状态情况,如:404-Not Found。HTTP 状态码和 json 结果中的状态码,并存尚可,用于体现不同维度的状态。

6.4 什么是 Swagger

相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。

其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。

发现了痛点就要去找解决方案。解决方案用的人多了,就成了标准的规范,这就是 Swagger 的由来

通过这套规范,你只需要按照它的规范去定义接口及接口相关的信息。再通过 Swagger 衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,生成多种语言的客户端和服务端的代码,以及在线接口调试页面等等。

这样,如果按照新的开发模式,在开发新版本或者迭代版本的时候,只需要更新 Swagger 描述文件,就可以自动生成接口文档和客户端服务端代码,做到调用端代码、服务端代码以及接口文档的一致性。

所以,Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful 风格的 Web 服务。

总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许 API 来始终保持同步。Swagger 让部署管理和使用功能强大的 API 从未如此简单。

6.5 Swagger 使用

Furion 框架提供了非常方便且灵活的 Swagger 配置,无需增加额外学习成本。

6.5.1 注册服务

备注

.UseInject() 已经包含了 .UseSpecificationDocuments() 注册,无需再次注册。

Furion.Web.Core\FurWebCoreStartup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Furion.Web.Core
{
[AppStartup(800)]
public sealed class FurWebCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSpecificationDocuments();
services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Other Codes

app.UseSpecificationDocuments();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
小知识

services.AddSpecificationDocuments() 通常和 .AddDynamicApiControllers() 成对出现。

6.5.2 默认地址

Furion 框架中,默认 规范化文档 地址为 /api 目录,支持自定义配置

如下图所示:

可以通过两种方式配置:

  • app.UseInject("路由") 方式,如
app.UseInject("testapi"); // 那么  /testapi 就是规范化地址
  • 配置文件配置
{
"SpecificationDocumentSettings": {
"RoutePrefix": "testapi"
}
}

配置文件优先级大于 UseInject() 方式

6.5.3 默认分组

Furion 框架中默认分组名为 Default支持自定义配置

{
"SpecificationDocumentSettings": {
"DefaultGroupName": "MyGroup"
}
}

6.5.4 文档注释

规范化文档默认扫描 Furion.ApplicationFurion.Web.CoreFurion.Web.Entry 三个程序集.xml 注释文件,支持自定义配置

只支持 /// 标识的注释语法,如:类、方法、属性、参数、返回值、验证特性

using Furion.DynamicApiController;

namespace Furion.Application
{
/// <summary>
/// 类注释
/// </summary>
public class FurionAppService : IDynamicApiController
{
/// <summary>
/// 方法注释
/// </summary>
/// <returns></returns>
public string Get()
{
return nameof(Furion);
}

/// <summary>
/// 带 ID 参数的方法注释
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public int Get(int id)
{
return id;
}
}
}

如下图所示:

小提示

如果文档注释没有显示,请检查项目 属性->生成->输出 中 XML 文档是否配置输出路径。注意:只有不带路径的 【项目名称.xml】 才会自动加载。

特别说明

Debug 模式下和 Release 模式下的注释文件是不通用的,所以导致很多开发者发布到服务器上发现没有显示注释。我们只需要在 Visual Studio 中切换 Debug 模式为 Release,然后重新配置一次即可。

这样不管是 Debug 还是 Release 模式都会显示注释了。

6.5.5 多分组支持

多分组是一个系统中必备功能,我们可以将系统划分为多个模块,每个模块都独立的 api 配置。在 Furion 框架中,实现多分组非常简单。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings("Group1")]
public class FurionAppService : IDynamicApiController
{
/// <summary>
/// 随父类 Group1 分组
/// </summary>
/// <returns></returns>
public string Post()
{
return nameof(Furion);
}

/// <summary>
/// 在 Group1、Group3 都有我
/// </summary>
/// <returns></returns>
[ApiDescriptionSettings("Group1", "Group3")]
public string Get()
{
return nameof(Furion);
}

/// <summary>
/// 我只在 Group2 出现
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[ApiDescriptionSettings("Group2")]
public int Get(int id)
{
return id;
}
}
}

如下图所示:

6.5.6 多分组排序

通过分组名称添加 @整数 进行排序

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings("Group1@1")]
public class FurionAppService : IDynamicApiController
{
public string Post()
{
return nameof(Furion);
}

[ApiDescriptionSettings("Group1", "Group3")]
public string Get()
{
return nameof(Furion);
}

[ApiDescriptionSettings("Group@2")]
public int Get(int id)
{
return id;
}
}
}

可以通过在分组名后面添加 @整数 进行排序,整数 越大排前面。如果分组名称多次指定且多次指定了 @整数 ,则自动取该分组最大的整数进行排序。

如下图所示:

排序说明

分组默认排序 Order0。如果同时配置了 @整数appsettings.json 配置文件,那么优先采用 appsettings.json 中的 Order

6.5.7 多分组信息配置

Furion 框架提供了可通过 appsetting.json 配置分组信息:

Furion.Web.Entry/appsettings.json
{
"SpecificationDocumentSettings": {
"GroupOpenApiInfos": [
{
"Group": "Group1",
"Title": "分组标题",
"Description": "这里是分组描述",
"Version": "版本号",
"TermsOfService": "https://furion.icu",
"Contact": {
"Name": "百小僧",
"Url": "https://gitee.com/monksoul",
"Email": "monksoul@outlook.com"
},
"License": {
"Name": "MulanPSL-2.0",
"Url": "https://gitee.com/dotnetchina/Furion/blob/alpha/LICENSE"
}
}
]
}
}

如下图所示:

6.5.8 组中组(标签)

Tag 配置主要用于配置 Swagger 标签分组信息及合并标签。也就是 组中组:

未贴标签之前

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}

public class TestAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}
}

贴标签之后

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(Tag = "分组一")]
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}

[ApiDescriptionSettings(Tag = "分组二")]
public class TestAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}
}

如下图所示:

小知识

如果 Tag 名字一样,则会自动合并,否则只是命名。

6.5.9 默认展开所有文档

Furion.Web.Entry/appsettings.json
{
"SpecificationDocumentSettings": {
"DocExpansionState": "Full"
}
}

如下图所示:

DocExpansionState 配置说明:

  • List:列表式(展开子类),默认值
  • Full:完全展开
  • None:列表式(不展开子类)

6.5.10 配置文档标题

Furion.Web.Entry/appsettings.json
{
"SpecificationDocumentSettings": {
"DocumentTitle": "我是自定义标题"
}
}

如下图所示:

6.5.11 授权控制

新版本 Furion 已经默认启用了 Bearer Token 授权配置,无需手动配置,如需手动配置,可手动添加以下类似配置:

Furion.Web.Entry/appsettings.json
{
"SpecificationDocumentSettings": {
"EnableAuthorized": true,

"SecurityDefinitions": [
{
"Id": "Bearer",
"Type": "Http",
"Name": "Authorization",
"Description": "JWT Authorization header using the Bearer scheme.",
"BearerFormat": "JWT",
"Scheme": "bearer",
"In": "Header",

"Requirement": {
"Scheme": {
"Reference": {
"Id": "Bearer",
"Type": "SecurityScheme"
},
"Accesses": []
}
}
}
]
}
}

6.5.12 在线测试

如下图所示:

6.5.13 性能监视 MiniProfiler

规范化文档默认集成了 MiniProfiler 第三方性能组件,通过该组件可以方便查看请求性能、异常堆栈、数据库操作等信息。默认在 Swagger 首页左上角显示。

如下图所示:

小提示

也可以通过 appsetting.jsonAppSettings:InjectMiniProfiler 设为 false 关闭。

6.5.14 定义接口输出类型

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[ProducesResponseType(201, Type = typeof(TestDto))]
[ProducesResponseType(400)]
public string Get()
{
return nameof(Furion);
}
}
}

如下图所示:

6.5.15 隐藏特定分组

Furion 新版本提供了隐藏分组的 Visible 配置,设置为 false 之后该分组将不显示在规范化文档中,如:

appsetting.json
{
"SpecificationDocumentSettings": {
"GroupOpenApiInfos": [
{
"Group": "Group1",
"Visible": false
}
]
}
}

6.5.16 中文乱码问题

默认情况下,.json 文件并未采用 utf-8 编码,所以如果配置中文分组信息就会出现乱码情况,这时候,只需要修改 .json 文件编码为 utf-8 即可。

6.5.17 生产环境中关闭 Swagger

如果不需要线上环境开启 Swagger 功能,只需要在 appsetting.json 配置即可:

{
"AppSettings": {
"InjectSpecificationDocument": false
}
}

6.5.18 设置 Example Value 默认值

Swagger 会自动根据对象类型输入参数添加 Example Value 默认值,但是该默认值通常是对象属性的类型字符串或缺省值,如果我们需要自定义这些默认值,只需要添加 /// <example>默认值</example> 注释即可。

如:

/// <summary>
/// 年龄
/// </summary>
/// <example>13</example>
[Required, Range(10, 110)]
public int Age { get; set; }

如下图所示:

更多类型默认值设置示例:

  • bool 类型:/// <example>true</example>
  • string 类型:/// <example>foobar</example>
  • number 类型:/// <example>123</example>
  • null 类型: /// <example>null</example>
  • array 类型:/// <example>[ 1, 2, 3 ]</example>
关于 object 类型输入参数

默认情况下,Example Value 不会显示 object 类型的对象属性,因为 Swagger 认为这是不合理的定义。如果需要强制显示,只需要添加 /// <example>"object"</example> 注释即可。

6.5.19 自定义 Swagger 配置

Furion 框架除了内置了不少配置以外,还提供了直接配置 Swagger Api 的参数,如:

public void ConfigureServices(IServiceCollection services)
{
services.AddInject(options =>
{
options.SpecificationDocumentConfigure = spt =>
{
spt.SwaggerGenConfigure = gen =>
{
// 配置 Swagger Gen
};
};
});
}
 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseInject(configure: options =>
{
options.SpecificationDocumentConfigure = spt =>
{
spt.SwaggerConfigure = swg =>
{
// 配置 Swagger Options
};
spt.SwaggerUIConfigure = ui =>
{
// 配置 Swagger UI
};
};
});
}

6.5.20 配置 SwaggerSchemes

Furion 框架默认只显示名称,如果需要自定义显示规则,只需要添加配置即可。

services.AddControllersWithViews()
.AddInject(options =>
{
options.SpecificationDocumentConfigure = spt =>
{
spt.SwaggerGenConfigure = gen =>
{
gen.CustomSchemaIds(x => x.FullName);
};
};
});

6.5.21 自定义 swagger.json 路由模板

默认情况下,Furion 框架会生成统一的分组模板,如:swagger/{documentName}/swagger.json{documentName} 会在运行时替换为分组名,如需自定义,配置 RouteTemplate 即可:

appsetting.json
{
"SpecificationDocumentSettings": {
"RouteTemplate": "myapp/{documentName}/xxxx.json"
}
}

6.5.22 关于 application/x-www-form-urlencoded 请求

默认情况下,Swagger 并未添加 application/x-www-form-urlencoded 支持,如需启用该配置,只需在方法顶部贴 [Consumes] 特性即可,如:

[Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> Test([FromForm] TestRequest testRequest)
{
// ....
}

public class TestRequest
{
public string TestValue { get; set; }
}
特别注意

参数必须贴 [FromForm] 特性。另外请求时将参数按 URL 地址拼接并放在 Body 中请求。

6.5.23 Swagger 出现 CORS 问题解决

如果 Swagger 出现以下错误,如图:

则需要添加以下配置:

{
"SpecificationDocumentSettings": {
"HideServers": true
}
}

6.5.24 Swagger 出现默认 xml 参数问题解决

产生此原因有两个必要条件:

  1. 使用了 Microsoft.AspNetCore.Mvc.NewtonsoftJson 包并添加了 AddNewtonsoftJson() 注册。
  2. .AddNewtonsoftJson() 写在了 .AddInjectWithUnifyResult() 后面。

所以解决方法是,先注册 .AddNewtonsoftJson() 再注册规范化结果,如:

services.AddControllers()
.AddNewtonsoftJson()
.AddInjectWithUnifyResult();

6.5.25 Swagger 多语言支持

Furion 2.9.0 + 版本已经支持了 Swagger 文档地址 ?culture=en-US 参数多语言转发功能了,也就是 Swagger 地址带 ?culture= 参数将自动添加到每一个请求的 api 地址中。

6.5.26 自定义逻辑控制 Swagger 每一个 api 可见性

有时候我们需要自定义 Swagger 接口可见性,比如根据权限,不同用户类型,各种逻辑控制,如:

// 也可以用 .AddInjectWithUnifyResult
services.AddInject(options =>
{
options.SpecificationDocumentConfigure = spt =>
{
spt.SwaggerGenConfigure = gen =>
{
gen.DocInclusionPredicate((currentGroup, apiDescription) =>
{
// Furion 内部检查,必须放第一行
var isShow = SpecificationDocumentBuilder.CheckApiDescriptionInCurrentGroup(currentGroup, apiDescription);

// 获取当前方法
_ = apiDescription.TryGetMethodInfo(out var method);

// 有了方法,这里做你想做的事情,isShow 设置 true 可见,设置 false 不可见

return isShow;
});
};
};
});

6.5.27 配置 MVC 控制器支持规范化处理

{
"UnifyResultSettings": {
"SupportMvcController": true
}
}

6.6 SpecificationDocumentSettings 配置

除了上述例子外,Furion 提供了一些配置选项,如:

  • DocumentTitle:文档标题,string,默认 Specification Api Document
  • DefaultGroupName:默认分组名,string,默认 Default
  • EnableAuthorized:是否启用权限控制,bool,默认 true
  • FormatAsV2:采用 Swagger 2.0 版本,bool,默认 false 已弃用
  • RoutePrefix:规范化文档地址,string,默认 api如果希望在首页,改为空字符串即可
  • DocExpansionState:文档显示方式,DocExpansion,默认 List,取值:
    • List:列表式(展开子类),默认值
    • Full:完全展开
    • None:列表式(不展开子类)
  • XmlComments:程序集注释描述文件名(可带 .xmlstring,默认 Furion.Application, Furion.Web.Entry, Furion.Web.Core
  • GroupOpenApiInfos:分组信息配置,SpecificationOpenApiInfo[],默认 { 'Group': 'Default'}
  • SecurityDefinitions:安全策略定义配置,SpecificationOpenApiSecurityScheme[],默认 []
  • Servers:配置 Server 下拉列表,OpenApiServer[] 类型,默认 [],如:{Servers:[ { Url:"地址", Description:"描述"} ]}
  • HideServers:是否隐藏 Server 下拉列表,bool 类型,默认 true
  • RouteTemplate:配置文档 swagger.json 路由模板,默认模板:swagger/{documentName}/swagger.json, {documentName} 代表分组名,必须保留原样
  • PackagesGroups:配置模块化内置分组名称,string[] 类型,默认 []
  • EnableEnumSchemaFilter:启用枚举 Schema 筛选器,bool 类型,默认 true
  • EnableTagsOrderDocumentFilter:启用标签排序筛选器,bool 类型,默认 true

另外 SpecificationOpenApiInfo 内置配置如下:

  • Group:分组唯一标识,string 类型,必填
  • Order:分组排序,int 类型,数字越大排前面,默认 0
  • Visible:配置分组是否可见,bool 类型,默认 true
  • Title:配置分组标题,string 类型
  • Description:配置分组描述,string 类型
  • Version:配置分组版本,默认 1.0
  • TermsOfService:配置相关链接地址,Uri 类型
  • Contact:配置联系方式,OpenApiContact 类型
  • License:配置协议,OpenApiLicense 类型

配置示例:

{
"SpecificationDocumentSettings": {
"GroupOpenApiInfos": [
{
"Group": "Group1",
"Title": "分组标题",
"Description": "这里是分组描述",
"Version": "版本号",
"TermsOfService": "https://furion.icu",
"Contact": {
"Name": "百小僧",
"Url": "https://gitee.com/monksoul",
"Email": "monksoul@outlook.com"
},
"License": {
"Name": "MulanPSL-2.0",
"Url": "https://gitee.com/dotnetchina/Furion/blob/alpha/LICENSE"
}
}
]
}
}

6.7 统一返回值模型/规范化结果/API 返回值

Furion 框架提供 规范化结果 功能,可以通过实现 IUnifyResultProvider 提供器实现统一规范化返回值定制,如:RESTfulResultProvider

using Furion.DataValidation;
using Furion.DependencyInjection;
using Furion.UnifyResult.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;

namespace Furion.UnifyResult
{
/// <summary>
/// RESTful 风格返回值
/// </summary>
[SuppressSniffer, UnifyModel(typeof(RESTfulResult<>))]
public class RESTfulResultProvider : IUnifyResultProvider
{
/// <summary>
/// 异常返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, errors: metadata.Errors));
}

/// <summary>
/// 成功返回值
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}

/// <summary>
/// 验证失败返回值
/// </summary>
/// <param name="context"></param>
/// <param name="metadata"></param>
/// <returns></returns>
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(StatusCodes.Status400BadRequest, errors: metadata.ValidationResult));
}

/// <summary>
/// 特定状态码返回值
/// </summary>
/// <param name="context"></param>
/// <param name="statusCode"></param>
/// <param name="unifyResultSettings"></param>
/// <returns></returns>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
{
// 设置响应状态码
UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);

switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "401 Unauthorized")
, App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, errors: "403 Forbidden")
, App.GetOptions<JsonOptions>()?.JsonSerializerOptions);
break;
default: break;
}
}

/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode"></param>
/// <param name="succeeded"></param>
/// <param name="data"></param>
/// <param name="errors"></param>
/// <returns></returns>
private static RESTfulResult<object> RESTfulResult(int statusCode, bool succeeded = default, object data = default, object errors = default)
{
return new RESTfulResult<object>
{
StatusCode = statusCode,
Succeeded = succeeded,
Data = data,
Errors = errors,
Extras = UnifyContext.Take(),
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
}
}
}

之后在 Startup.cs 中注册即可:

services.AddControllers()
.AddInjectWithUnifyResult<RESTfulResultProvider>();
特别注意

默认情况下,规范化结果不会对 401403404 状态码进行规范化处理,如需启动该状态码处理,只需在 Startup.cs 中的 Configure 中启用接口:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 添加规范化结果状态码,需要在这里注册
app.UseUnifyResultStatusCodes();
// 其他注册...
}

6.7.1 排除规范化处理

有些时候,我们某些接口不需要进行规范化处理,这时,我们只需要帖 [NonUnify] 特性即可。

6.7.2 规范化结果添加额外数据

默认的规范化结果中包含 extras 属性,可以配置额外数据返回到客户端:

UnifyContext.Fill(new { Message = "操作成功" });

6.7.3 自定义特别接口规范化结果

有些时候,我们特定接口需返回特定的接口类型,无需规范化处理,这时候我们只需要贴 [UnifyResult(typeof(结果类))][ProducesResponseType(typeof(结果类),200)],如:

[UnifyResult(typeof(Person))]
public Person GetPerson(int id)
{
// ...
}

6.8 反馈与建议

与我们交流

给 Furion 提 Issue