> For the complete documentation index, see [llms.txt](https://monksoul.gitbook.io/hoa/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://monksoul.gitbook.io/hoa/kaifangjiekouzhinan/10.1restful-he-swagger.md).

# 10.1、RESTFul 和 Swagger

## 什么是 Web API

WebAPI 是一种用来开发系统间接口、设备接口 API 的技术，基于 Http 协议，请求和返回格式结果默认是 json 格式。

WebAPI 比 WCF 更简单、更通用，比 WebService 更节省流量、更简洁。

## 什么是 RESTFul 风格

REST，即Representational State Transfer的缩写。直接翻译的意思是"表现层状态转化"。 它是一种互联网应用程序的API设计理念：URL定位资源，用HTTP动词（GET，POST，DELETE，PUT，HEAD）描述操作。

RESTful 架构服务器上每一种资源，比如一个文件，一张图片，一部电影，都有对应的URL地址，如果我们的客户端需要对服务器上的这个资源进行操作，就需要通过HTTP协议执行相应的动作来操作它，比如进行获取，更新，删除。

简单来说，就是URL地址中只包含名词表示资源，使用HTTP动词表示动作进行操作资源。

## 什么是 [Swagger](https://swagger.io/)

> 相信无论是前端还是后端开发，都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力，经常来不及更新。其实无论是前端调用后端，还是后端调用后端，都期望有一个好的接口文档。但是这个接口文档对于程序员来说，就跟注释一样，经常会抱怨别人写的代码没有写注释，然而自己写起代码起来，最讨厌的，也是写注释。所以仅仅只通过强制来规范大家是不够的，随着时间推移，版本迭代，接口文档往往很容易就跟不上代码了。

发现了痛点就要去找解决方案。解决方案用的人多了，就成了标准的规范，这就是[Swagger](https://swagger.io/)的由来。

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

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

所以，[**Swagger**](https://swagger.io/) **是一个规范和完整的框架，用于生成、描述、调用和可视化 RESTful 风格的 Web 服务**。

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

简单来说，就是解决 RESTFul api 没有详细规范化的对接文档的痛点。

## 生成RESTFul 风格的Web API

在这现代化的应用开发过程中，最尤为突出的开发模式便是 **前后端分离** 技术及 **MVVM 双向绑定**开发模式。而支撑它们的正式 RESTFul 风格的盛行及规范化的对接方式。

所以，Hoa Framework 框架提供了非常强大且易配置的 RESTFul 风格生成引擎。

在 Hoa Framework 支持两种方式生成 RESTFul 风格的API方式，**一种是 ASP.NET Core 内置的 `ControllerBase` 控制器方式**，**一种是 框架内自实现的 动态控制器服务 方式**。

### ControllerBase 方式

ASP.NET Core 内置的 `ControllerBase` 方式只需要在 `Hoa.Web.Host.Controllers` 目录中定义即可，如：

```csharp
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Hoa.Web.Host.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HoaController : ControllerBase
    {
        [HttpGet]
        [Route(nameof(Author))]
        public string Author()
        {
            return "Powered by Monk";
        }

        [HttpGet]
        [Route(nameof(Name))]
        public IActionResult Name()
        {
            return Content("Hoa");
        }

        [HttpPost]
        [Route("GetDatas/{keyword}")]
        public IEnumerable<object> GetDatas([Required] string keyword)
        {
            // TODO

            return null;
        }
    }
}
```

框架内部也对ASP.NET Core 内置的 Web API模式做了加强，支持Swagger文档生成、多应用权限、文档分类等强大的功能。

更多 ASP.NET Core RESTFul 风格 WebAPI 方式[可查看官方文档](https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-3.1)。

### [动态控制器服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu) 方式（推荐）

[动态控制器服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu)是 Hoa Framework 一项非常强大的功能，大大提升了我们开发的速度和避免了很多配置WebAPI 的常见低级错误。既兼容了 **ASP.NET Core 内置的 Web API模式** 而且支持Swagger文档生成、多应用权限、文档分类等强大的功能，页提供了非常强大的拦截器操作。**是目前构建 WebAPI首选方式**。

关于 [动态控制器服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu) 介绍可查看 章节：[七、控制器和服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu)。

#### :flag\_black: 示例一，将 [普通服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#pu-tong-fu-wu) 变为 [动态控制器服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu)

只需要在 [普通服务](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#pu-tong-fu-wu) 类声明地方贴 `[HoaServiceController]` 特性，并继承 `IAppServiceDependency` 即可。

**默认情况下，**[**动态控制器服务**](https://monksoul.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu) **会将 所有公开的实例方法生成对应的控制器Action，**&#x4F46;不会对私有方法和静态方法做生成操作。

```csharp
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;

namespace Hoa.Application.Authorization
{
    [HoaServiceController]  // 贴了特性，动态控制器服务
    public class TestAppService : ITestnAppService, IAppServiceDependency  // 继承了 IAppServiceDependency
    {
        // 普通服务
        privde readonly ISomeService _service;
        privde readonly ISomeService _service2;
        public TestAppService(ISomeService service, ISomeService service2)
        {
            _service = service;
            _service2 = service;
        }

        // Other Codes
    }
}
```

#### :flag\_black: 示例二，手动配置实例方法不生成对应的控制器Action

只需要在方法申明顶部贴 `[HoaNonServiceWebApi]` 或 `[HoaServiceWebApiExplorer(false)]` 即可，如：

```csharp
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;

namespace Hoa.Application.Authorization
{
    [HoaServiceController]  // 贴了特性，动态控制器服务
    public class TestAppService : ITestAppService, IAppServiceDependency  // 继承了 IAppServiceDependency
    {
        public TestAppService()
        {
        }

        // 会生成控制器对应的Action
        public string GetName(){
            return "Hoa";
        }
        
        // 不会生成控制器对应的Action
        [HoaNonServiceWebApi]
        public string NoExportGetName(){
            return "No Export";
        }
        
        // 不会生成控制器对应的Action
        [HoaServiceWebApiExplorer(false)]
        public string NoExportGetName2(){
            return "No Export";
        }
    }
}
```

**注意：如果在类上面贴`[HoaNonServiceWebApi]` 或 `[HoaServiceWebApiExplorer(false)]`，则这个类的所有方法包含自身都不会生成对应的控制器实例。**

#### :flag\_black: **示例三，配置Http Method生成特定请求的控制器Action**&#x20;

```csharp
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;

namespace Hoa.Application.Authorization
{
    [HoaServiceController]  // 贴了特性，动态控制器服务
    public class TestAppService : ITestAppService, IAppServiceDependency  // 继承了 IAppServiceDependency
    {
        public TestAppService()
        {
        }

        [HttpGet]
        public string GetName(){
            return "Hoa";
        }
        
        [HttpPost]
        public string Create(){
            // TODO
        }
        
        [HttpPut]
        public string Update(){
            // TODO
        }
        
        [HttpDelete]
        public string Delete(){
            // TODO
        }
        
        [AcceptVerbs("GET","POST")]
        pub string GetOrPost(){
            // TODO
        }
    }
}
```

**默认情况下，Hoa Framework 内置了一套生成 HTTP Method 谓词的方法**，生成规则如下：

| 方法名首单词          | 生成对应的HTTP Method 谓词 |
| --------------- | ------------------- |
| `add/Add`       | `[HttpPost]`        |
| `create/Create` | `[HttpPost]`        |
| `post/Post`     | `[HttpPost]`        |
| `insert/Insert` | `[HttpPost]`        |
| `get/Get`       | `[HttpGet]`         |
| `find/Find`     | `[HttpGet]`         |
| `fetch/Fetch`   | `[HttpGet]`         |
| `query/Query`   | `[HttpGet]`         |
| `update/Update` | `[HttpPut]`         |
| `put/Put`       | `[HttpPut]`         |
| `delete/Delete` | `[HttpDelete]`      |
| `remove/Remove` | `[HttpDelete]`      |
| **缺省**          | `[HttpPost]`        |

#### :flag\_black: 示例四，设置方法已过时

```csharp
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;

namespace Hoa.Application.Authorization
{
    [HoaServiceController]  // 贴了特性，动态控制器服务
    public class TestAppService : ITestAppService, IAppServiceDependency  // 继承了 IAppServiceDependency
    {
        public TestAppService()
        {
        }
        
        // 提示接已过时
        [Obsolete("请调用/api/Test/GetWithPost接口")]
        pub string GetOrPost(){
            // TODO
        }
    }
}
```

#### :flag\_black: 示例五，配置URL路由地址

```csharp
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;

namespace Hoa.Application.Authorization
{
    [HoaServiceController]  // 贴了特性，动态控制器服务
    public class TestAppService : ITestAppService, IAppServiceDependency  // 继承了 IAppServiceDependency
    {
        public TestAppService()
        {
        }
        
        [Route("/api/Groups/GetPost")]
        pub string GetOrPost(){
            // TODO
        }
    }
}
```

可以通过 `[Route(url)]` 方式覆盖默认生成的路由，默认生成的路由格式为：**`/api/{区域}/{类名}/{方法名}/{值类型参数}`**

详细大家看到这里的时候已经很清楚了，[动态控制器服务](https://snrc.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu) 实际和 `ControllerBase` 的实现方式非常类似，没错，[动态控制器服务](https://snrc.gitbook.io/hoa/kongzhiqihefuwu#dong-tai-kong-zhi-qi-fu-wu) 就是将我们的 [普通服务](https://snrc.gitbook.io/hoa/kongzhiqihefuwu#pu-tong-fu-wu) 变成了 `ApiController`，此时共享 `ControllerBase` 支持的所有功能和特性。

## [Swagger](https://swagger.io/) 功能配置

通过上面第二小节 [什么是 Swagger ](https://snrc.gitbook.io/hoa/kaifangjiekouzhinan#shen-me-shi-swagger)我们已经了解了 [Swagger](https://swagger.io/) 的用处，在 Hoa Framework 框架中，除了 [Swagger](https://swagger.io/) 自带的功能之外，还拓展了很多非常方便而且具有特殊的功能，比如 **数据验证、多系统应用授权、多文档分组、性能分析**等等。

在 Hoa Framework 中，默认已经启用了 [Swagger](https://swagger.io/) 功能的支持，也就是无需手动配置。再者，我们只需要简单的贴特性的方式就可以生成非常丰富的 RESTFul 风格的文档了！

### 文档默认地址

Swagger 默认的文档链接地址是在域名的根目录下，如：`https://你的主机/` 。

![](/files/-M7urCmwlI2dJtUOQnCa)

### 设置文档信息

设置文档信息只需要修改 `appsetting.json` `HoaSwaggerOptions` 节点即可，如果文档有多个分类，只需要在当前节点的 `Groups` 节点中添加多个分类信息即可。如：

```javascript
"HoaSwaggerOptions": {
    "DocumentTitle": "Hoa RESTFul API",  // 文档浏览器标题
    "SecurityDefinitionName": "Bearer",  // 默认JWT 验证方式 Bearer
    "UnClassifiedName": "Default",  // 未分类文档默认分组名称
    "IsShowMSTestSummary": true,  // 是否显示单元测试汇总
    "Groups": {  // 分类文档信息配置，支持多个，Key 为分类分组名称，如：Default，AuthServer
    
     // 默认分类文档分组名 
      "Default": {  
        "Title": "Default API",  // 分类标题
        "Description": "This is default restful api description.",  // 分类描述
        "TermsOfService": "",  // 协议信息
        "Contact": {  // 联系方式
          "Name": "Monk",  // 联系人
          "Email": "monksoul@outlook.com",  // 联系人电子邮件
          "Url": ""  // 联系人个人主页
        },
        "License": {  // 许可证
          "Name": "",  // 许可证名称
          "Url": ""  // 许可证Url
        }
      },
      
      // 自定义分组名
      "AuthServer": {
        "Title": "AuthServer API",  // 分类标题
        "Description": "This is authServer restful api description.",  // 分类描述
        "TermsOfService": "",  // 协议信息
        "Contact": {  // 联系方式
          "Name": "Monk",  // 联系人
          "Email": "monksoul@outlook.com",  // 联系人电子邮件
          "Url": ""  // 联系人个人主页
        },
        "License": {  // 许可证
          "Name": "",  // 许可证名称
          "Url": ""  // 许可证Url
        }  
      }
    }
  }
```

![](/files/-M7ur3Q2xWGsJVCJAArW)

### 生成文档注释

```csharp
using Hoa.Application.Authorization.Dtos;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;


namespace Hoa.Application.Authorization
{
    // 设置分组名称，建议使用常量配置起来
    [HoaServiceWebApiExplorer("AuthServer")]
    [HoaServiceController]
    public class AuthorizationAppService : IAuthorizationAppService, IAppServiceDependency
    {
        /// <summary>
        /// 登录操作
        /// </summary>
        /// <param name="input">登录参数</param>
        /// <returns>登录信息</returns>
        public SignInOutput SignIn(SignInInput input)
        {
            // TODO
        }
    }
}
```

![](/files/-M7usF8MQFWe-JW3BwDr)

### 生成参数验证

Hoa Framework 内置的 [Swagger](https://swagger.io/) 支持 ASP.NET Core 默认的 [模型验证](https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-3.1) 方式和强大的 [FluentValidation](https://fluentvalidation.net/) 两种模式。详细的 模型验证 将在第 [十一、模型验证](https://monksoul.gitbook.io/hoa/shujujiaoyan) 章节说明。

#### :flag\_black: 配置模型验证

```csharp
using FluentValidation;
using System.ComponentModel.DataAnnotations;

namespace Hoa.Application.Authorization.Dtos
{
    public class SignInInput
    {
        [Required]    // 必填
        [EmailAddress]    // 邮件地址
        public string Email { get; set; }

        [Required]    // 必填
        [MinLength(5)]    // 最小长度
        public string Password { get; set; }

        [Required]    // 必填
        public UserType UserType { get; set; }
    }

    // FluentValidation 方式
    public class SignInInputValidator : AbstractValidator<SignInInput>
    {
        public SignInInputValidator()
        {
            RuleFor(x => x.Email).Length(20, 250).WithMessage("长度在 20-250之间。");
        }
    }
}
```

![](/files/-M7uu1TNAHzOebvjlpPv)

### 生成多套分类文档

在 Hoa Framework 中，集成了非常强大的多分类文档生成器，只需要在**类或方法**上贴 `[[HoaServiceWebApiExplorer(分组名称, 分组名, ...)]` 即可，**支持同一个接口生成多分组共享功能**。该特性能够让我们的 RESTFul API 具有更具清晰的归类，是多系统统一API不二之选！ :call\_me: :heart\_eyes:&#x20;

#### :flag\_black: 生成或合并到新的分组，支持类和方法

```csharp
using Hoa.Application.Authorization.Dtos;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;


namespace Hoa.Application.Authorization
{
    [HoaServiceWebApiExplorer("AuthServer")]
    [HoaServiceController]
    public class AuthorizationAppService : IAuthorizationAppService, IAppServiceDependency
    {
        public AuthorizationAppService()
        {
 
        }

        [HoaServiceWebApiExplorer("AuthServer")]
        public SignInOutput SignIn(SignInInput input)
        {
            // TODO
        }
        
        // 会覆盖父类，而且会出现在 "Default" 分类中
        [HoaServiceWebApiExplorer("Default")]
        public SignInOutput DefaultSignIn(SignInInput input)
        {
            // TODO
        }
    }
}
```

![](/files/-M7vAMQitMGZ9YLliEpJ)

#### :flag\_black: 分组之间共享接口文档

```csharp
using Hoa.Application.Authorization.Dtos;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;


namespace Hoa.Application.Authorization
{
    [HoaServiceWebApiExplorer("AuthServer")]
    [HoaServiceController]
    public class AuthorizationAppService : IAuthorizationAppService, IAppServiceDependency
    {
        public AuthorizationAppService()
        {
 
        }

        [HoaServiceWebApiExplorer("AuthServer")]
        public SignInOutput SignIn(SignInInput input)
        {
            // TODO
        }
        
        // 会出现在 "Default" 和 "AuthServer" 两个分类中，实现共享
        [HoaServiceWebApiExplorer("Default","AuthServer")]
        public SignInOutput DefaultSignIn(SignInInput input)
        {
            // TODO
        }
    }
}
```

![](/files/-M7vB85IF1ED01OQJ9-O)

### 生成同一个方法多个版本

```
using Hoa.Application.Authorization.Dtos;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;


namespace Hoa.Application.Authorization
{
    [HoaServiceWebApiExplorer("AuthServer")]
    [HoaServiceController]
    public class AuthorizationAppService : IAuthorizationAppService, IAppServiceDependency
    {
        public AuthorizationAppService()
        {
 
        }

        public SignInOutput SignIn(SignInInput input)
        {
            // TODO
        }
        
        [HoaExportVersion("v2")]
        public SignInOutput SignIn_v2(SignInInput input)
        {
            // TODO
        }
    }
}
```

![](/files/-MA_tfq7n6MdlbRZCmzo)

### 支持多个请求方法访问同一个接口

```csharp
[AcceptVerbs("GET")]
[AcceptVerbs("POST")]
```

### 强大的性能分析，SQL打印、异常输出

性能分析是 Hoa Framework 中 Swagger 文档一大特色，能够让我们看到实时的性能情况以及执行的数据库操作语句。是开发高质量的应用程序必备功能！

#### :flag\_black: 性能监控

![](/files/-M7vC7BgDCGYjSCOpJ2N)

#### :flag\_black: SQL打印

![](/files/-M7vCf2RmYRP1-Jx7sGK)

支持EF Core、ADO.NET、存储过程、函数等所有数据操作监听！

#### :flag\_black: 异常打印

![](/files/-M7vD62ItfCUq6UDVy5U)

### 默认授权/JWT授权/策略授权/自定义授权

保护接口合理访问是我们必备的功能，Hoa Framework 内置了极其强大且非常易用的授权机制，只需要简单贴特性即可完整复杂的授权操作，包括角色授权、JWT授权、等任何自定义授权。

ASP.NET Core 自带授权方式

```csharp
using Hoa.Application.Authorization.Dtos;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using System;


namespace Hoa.Application.Authorization
{
    [HoaServiceWebApiExplorer("AuthServer")]
    [HoaServiceController]
    public class AuthorizationAppService : IAuthorizationAppService, IAppServiceDependency
    {
        public AuthorizationAppService()
        {
 
        }

        // 可匿名访问
        [AllowAnonymous]
        public SignInOutput SignIn(SignInInput input)
        {
            // TODO
        }
        
        // 必须授权才能访问
        [Authorize]
        public UserInfo GetUserInfo([Required] int userId)
        {
            // TODO
        }
        
        // 非常强大的多系统授权方式，支持多系统授权
        [HoaMultipleClassifyAuthorize("A系统密钥","B系统密钥",...)]
        public UserInfo GetUserInfo([Required] int userId)
        {
            // TODO
        }
    }
}
```

![](/files/-M7vEnvb10rx8M_2OEaC)

更多授权知识可查看 [十二、安全授权](https://monksoul.gitbook.io/hoa/anquanshouquan) 章节。

### 支持在线测试，无需 PostMan 了

![](/files/-M7vFuFWYu5xJ76Zvjez)
