从零开始:如何在.NET Core Web API中完美配置Swagger文档

目录

新建项目

RestFul

Swagger配置

注释展示

版本控制

Token传值

方法封装


新建项目

打开visual studio创建新项目,这里我们选择.net core web api模板,然后输入项目名称及其解决方案创建新项目

这里使用配置一些其他信息,根据自己情况进行选择:

创建好项目之后我们可以看到 web api 模板运行的项目,相比于MVC模式开发的项目,初始文件是保留原有的控制器文件但是删除了模型和视图文件,而且还加上了两个文件,文件的作用相当于测试用例一一可以模拟发起请求,如下所示:

我们直接运行该项目,可以看到我们打开了一个Swagger文件,对于后端小白来说可能不太熟悉这个文件的作用,但是对于前端开发者来讲可谓是非常熟悉的东西,前端开发调用接口就需要经常和这个文件打交道,如下所示:

我们除了可以在Swagger上测试接口,也可以在本地代码的测试用例处点击发起请求进行测试:

这里做一个演示,比如说我们想在Swagger文档中新增一组接口的话,我们可以在控制器文件中设置一下get、post、delete、put请求操作,如下我们将原本的文件复制一份然后命名为First,然后设置一下接口类型,如下所示:

重新运行我们的项目然后打开Swagger,可以看到我们创建的一组接口成功出现了,这里接口的组名可以看到是FirstController.cs 文件中Controller的前缀,非常好识别:

RestFul

背景:近些年来随着移动互联网的发展,前端设备层出不穷(手机、平板、桌面电脑、其他专用设备...),因此必须有一组统一的机制方便不同的前端设备与后端进行通信,于是RestFul诞生了,它可以通过一套统一的接口为Web,iOS和Android等各种各样的终端提供服务。

特点:RestFul把控制器作为维度当成一块资源,对于这个资源会进行增删改查等各种当作,这些动作必须都得有唯一的URL地址来匹配请求的Method的类型(get、post、put、delete),本质上就是用URL定位资源,用HTTP(get、post、put、delete)描述操作。说白了就是一种软件架构设计风格而不是标准,只是提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁有层次,更易于实现缓存等机制。

比如上文我们在控制器文件中设置了一组接口,如果我们再在文件中添加比如说一个get接口请求,运行项目其实是会报错的,为什么?就是因为这两个get请求的地址都是一样的,编辑器没法识别到底哪个get请求是你想要的,为了区分我们必须设置唯一的URL地址来避免报错,如下:

重新运行项目,可以看到我们在First组当中又新增了一个新的get请求,且两个get请求的URL路径是不一致的,如下所示:

对于路由约束的类型我们可以参考如下表格,这些都可以作为接口传参的一个类型限定:

限制

示例

匹配示例

说明

int

{id: int}

123456789, -123456789

匹配任何整数

bool

{active: bool}

true, false

匹配true或false,忽略大小写

datetime

{dob: datetime}

2024-12-11,2024-12-12 7:32pm

匹配满足datetime类型的值

decimal

{price: decimal}

49.99, -1,000.01

匹配满足decimal类型的值

double

{height: double}

1234, -1001.01e8

匹配满足double类型的值

float

{height: float}

1234, -1001.01e8

匹配满足float类型的值

long

{ticks: long}

123456789, -123456789

匹配满足long类型的值

minlength(value)

{usename: minlength(4)}

KOBE

字符串长度不能小于4个字符

maxlength(value)

{filename: maxlength(8)}

CURRY

字符串长度不能超过8个字符

length(value)

{filename: length(12)}

somefile.txt

字符串长度必须是12个字符

length(min,max)

{filename: length(8,16)}

somefile.txt

字符串长度必须介于8和16之间

min(value)

{age: min(18)}

20

整数值必须大于18

max(value)

{age: max(120)}

119

整数值必须小于120

range(min, max)

{age: range(18, 120)}

100

整数值必须介于18和120之间

alpha

{name: alpha}

Rick

字符串必须由一或多a-z字母组成

regex(expression)

{ssn:regex(^d{3})}

3

字符串必须匹配指定的正则

required

Swagger配置

虽然上面我设置了Swagger并成功运行了,但是设想一个场景,如果你是后端你写的代码你当然看的懂,生成的Swagger接口也是知道是什么意思,但是你把这个Swaager丢给前端,前端是否看的懂呢?如果看不懂是不是还要找后端沟通,这样就增加了时间成本。所以后端不仅仅要不接口写对,还要把Swagger文档写好,这里我们就需要对其进行相应配置,争取让其他人看懂自己写的接口,这才是真正意义上一名后端应该做的事情,如下所示:

注释展示

注释是很重要的,当我们写完一个接口之后我们就需要对这个接口的作用进行注释,用来告诉前端后端给的这个接口作用和意义是什么,如何设置Swagger注释展示呢?请往下看:

如下我们给接口代码写下注释,注释的快捷键是///,也就是三个斜杠即可生成,然后右键项目点击属性,勾选文档文件这个复选框:

我们右键项目重新生成解决方案然后打开我们的项目资源管理目录,可以看到在我们项目的obj目录下的最里层有生成了一个xml文件,右键选择文本打开可以看到我们的注释就在里面:

接下来如果说我们想把注释展示到Swagger文档中的话,我们需要来到入口文件Program.cs对我们的Swagger进行一个配置,给原本的AddSwaggerGen添加一个配置对象,代码如下:

复制代码
builder.Services.AddSwaggerGen(option => {
    #region 注释展示
    {
        // 获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
        string basePath = AppContext.BaseDirectory;
        string xmlPath = Path.Combine(basePath, "netCoreWebApi.xml");
        option.IncludeXmlComments(xmlPath);
    }
    #endregion
});

回到Swagger文档中可以看到我们的注释已经成功的被渲染出来了,方便前端的理解:

版本控制

随着项目不断升级和开发的过程中,我们还可能需要去对项目进行开发多个版本,但是版本之间的Swagger是不同的,不会放置在同一个Swagger文档下面,我们打开Swagger文档也能看到右上角有个下拉框,其就是支持不同的版本的切换的,如何做?请往下看:

为了方便不同版本的切换,这里我们直接在解决方案中新建项目设置一个独立的文件夹出来,因为待会我们还要对其作一个扩展,所以这里我们选择一个类库即可,如下:

生成的文件我们重命名为 ApiVersionInfo ,然后设置静态成员V1到V5表明版本有5个,如下所示:

我们来到入口文件Program.cs文件处,这里我们开始设置支持Swagger版本控制的代码,这里我们借助 System.Reflection 中的FieldInfo反射机制中的类型,来获取类型ApiVersionInfo中的所有字段信息,然后通过OpenApiInfo来描述Swagger文档的元数据及包含了有关API的关键信息,比如标题、版本、描述等,如下所示:

复制代码
builder.Services.AddSwaggerGen(option => {
    #region 注释展示
    {
        // 获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
        string basePath = AppContext.BaseDirectory;
        string xmlPath = Path.Combine(basePath, "netCoreWebApi.xml");
        option.IncludeXmlComments(xmlPath);
    }
    #endregion
    #region 支持Swagger版本控制
    {
        foreach (FieldInfo filed in typeof(ApiVersionInfo).GetFields())
        {
            option.SwaggerDoc(filed.Name, new OpenApiInfo()
            {
                Title = $"netCoreWebApi API {filed.Name}",
                Version = filed.Name,
                Description = $"netCoreWebApi API {filed.Name} 版本"
            });
        }
    }
    #endregion
});

接下来配置Swagger UI使得每个API版本都有一个对应的Swagger UI页面,具体来说它会为每个API版本动态创建一个Swagger UI的入口从而允许用户查看不同版本的API文档,如下:

接下来我们来到编写接口代码的控制器文件,给不同版本设置如下代码,作用是将特定的API控制器或者方法标记为属于某个版本组,从而使得Swagger等工具能够按版本组织和展示API文档:

最终呈现的效果如下所示:

Token传值

如果项目的接口需要授权才能被访问的话,这里我们就需要通过token传值的方式来进行鉴权才能调试接口,如何做呢?请往下看:

这里我们仍然在Swagger配置函数中设置token传值以及添加对应的安全要求,如下所示:

复制代码
#region 支持token传值
{
    option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
    {
        Description = "JWT授权(数据将在请求头中进行传输) 参数结构: "Authorization: Bearer {token}"", // 描述
        Name = "Authorization", // 授权名称
        In = ParameterLocation.Header, // 授权位置,放在头信息进行传值
        Type = SecuritySchemeType.ApiKey, // 授权类型
        BearerFormat = "JWT", // 格式是JWT
        Scheme = "Bearer"
    });
    // 添加安全要求
    option.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference()
                {
                    Id = "Bearer", // 必须和上面一致
                    Type = ReferenceType.SecurityScheme
                }
            },
            new string[] { }
        }
    });
}
#endregion

运行项目来到Swagger文档中,可以看到我们已经成功添加了身份验证,这里我们输入对应的JWT格式即可,如下所示我们输入token之后发起请求,如下已经带上了我们设置的token:

方法封装

根据上面对Swagger的配置,可以看到我们对Swagger的配置基本上都写在了入口Program.cs文件当中,这样就显得非常的冗余,入口文件代码量太多了,这里我们可以将对Swagger的配置抽离到我们创建的类库当中,新建一个文件夹用于专门存放Swagger配置的东西,如下所示:

这里设置一个函数然后把IServiceCollection设置为Service的类型,该类型用于配置依赖注入容器的接口,通常为Swagger配置服务,我们直接把入口文件当中配置Swagger的代码直接粘贴过来:

复制代码
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace netCoreWebApi.WebCore.SwaggerExt
{
    public static class SwaggerExtension
    {
        public static void AddSwaggerExtension(this IServiceCollection Service)
        {
            Service.AddEndpointsApiExplorer();
            Service.AddSwaggerGen(option => {
                #region 注释展示
                {
                    // 获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
                    string basePath = AppContext.BaseDirectory;
                    string xmlPath = Path.Combine(basePath, "netCoreWebApi.xml");
                    option.IncludeXmlComments(xmlPath);
                }
                #endregion
                #region 支持Swagger版本控制
                {
                    foreach (FieldInfo filed in typeof(ApiVersionInfo).GetFields())
                    {
                        option.SwaggerDoc(filed.Name, new OpenApiInfo()
                        {
                            Title = $"netCoreWebApi API {filed.Name}",
                            Version = filed.Name,
                            Description = $"netCoreWebApi API {filed.Name} 版本"
                        });
                    }
                }
                #endregion
                #region 支持token传值
                {
                    option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                    {
                        Description = "JWT授权(数据将在请求头中进行传输) 参数结构: "Authorization: Bearer {token}"", // 描述
                        Name = "Authorization", // 授权名称
                        In = ParameterLocation.Header, // 授权位置,放在头信息进行传值
                        Type = SecuritySchemeType.ApiKey, // 授权类型
                        BearerFormat = "JWT", // 格式是JWT
                        Scheme = "Bearer"
                    });
                    // 添加安全要求
                    option.AddSecurityRequirement(new OpenApiSecurityRequirement()
                    {
                        {
                            new OpenApiSecurityScheme
                            {
                                Reference = new OpenApiReference()
                                {
                                    Id = "Bearer", // 必须和上面一致
                                    Type = ReferenceType.SecurityScheme
                                }
                            },
                            new string[] { }
                        }
                    });
                }
                #endregion
            });
        }

        public static void UseSwaggerExtension(this WebApplication app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(option => {
                foreach (FieldInfo filed in typeof(ApiVersionInfo).GetFields())
                {
                    option.SwaggerEndpoint($"/swagger/{filed.Name}/swagger.json", filed.Name);
                }
            });
        }
    }
}

这里还需要在类库中安装一下主程序当中包,这里注意意义名称和版本号:

因为this修饰静态类中的静态方法,上面创建的两个方法都是静态类中的静态方法同时第一个参数还用this进行修饰,这种扩展方法可以把传参提到前面当成实例方法来进行调用,接下来我们就在入口文件中调用这两个方法:

最后我们重新运行项目,如下可以看到我们的Swagger也可以被重新运行:

相关推荐
Asort3 分钟前
JavaScript 从零开始(六):控制流语句详解——让代码拥有决策与重复能力
前端·javascript
无双_Joney21 分钟前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(功能篇)
前端·后端·nestjs
在云端易逍遥23 分钟前
前端必学的 CSS Grid 布局体系
前端·css
ccnocare25 分钟前
选择文件夹路径
前端
艾小码25 分钟前
还在被超长列表卡到崩溃?3招搞定虚拟滚动,性能直接起飞!
前端·javascript·react.js
闰五月26 分钟前
JavaScript作用域与作用域链详解
前端·面试
泉城老铁29 分钟前
idea 优化卡顿
前端·后端·敏捷开发
前端康师傅29 分钟前
JavaScript 作用域常见问题及解决方案
前端·javascript
司宸31 分钟前
Prompt结构化输出:从入门到精通的系统指南
前端