0


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

新建项目

打开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整数值必须大于18max(value){age: max(120)}119整数值必须小于120range(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也可以被重新运行:


本文转载自: https://blog.csdn.net/qq_53123067/article/details/144408118
版权归原作者 亦世凡华、 所有, 如有侵权,请联系我们删除。

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

还没有评论