.Net6 Api Swagger配置

第一种:自定义:

1、定义个Swagger版本(组)的枚举

namespace WebApp.Enums
{
    /// <summary>
    /// api版本枚举
    /// </summary>
    public enum ApiVersion
    {
        /// <summary>
        /// v1版本
        /// </summary>
        v1 = 1,
        /// <summary>
        /// v2版本
        /// </summary>
        v2 = 2,
    }
}

2、添加SwaggerExtentsion扩展类,配置注册Swagger

using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Net.Http.Headers;
using System.Net;
using System.Reflection;
using System.Text;
using WebApp.Enums;

namespace WebApp.Common.Swagger
{
    /// <summary>
    /// SwaggerDoc配置
    /// 用法:在Program.cs文件中进行注册:builder.AddSwaggerGenExt();
    /// </summary>
    public static class SwaggerExtension
    {
        /// <summary>
        /// 扩展方法:注入Swagger服务
        /// </summary>
        /// <param name="services"></param>
        public static void AddSwaggerGenExt(this IServiceCollection services)
        {
            #region 添加Swagger
            //获取的是当前执行的方法所在的程序文件的名称:即项目名称,例如我的项目名称叫webapp 
            var AssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            services.AddSwaggerGen(options =>
            {
                #region 配置版本
                //options.SwaggerDoc("v1", new OpenApiInfo { Title = "售楼API", Version = "v1" });
                typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                {
                    options.SwaggerDoc(version, new OpenApiInfo()
                    {
                        Version = version,
                        Title = "凤凰网管理系统",
                        Description = $"凤凰网接口服务    版本: {version}",
                        Contact = new OpenApiContact
                        {
                            Name = "潇湘夜雨",
                            Email = "123@qq.com"
                        }
                    });
                });

                #endregion
                #region 配置注释文档

                // 获取当前项目的 XML文档文件路径:比如我的项目名称叫WebApp,那么它默认的 XML文档文件路径就是当前项目下的 WebApp.xml
                var xmlFile = $"{AssemblyName}.xml";
                var xmlFileFullPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

                //var domainXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.Domain.xml");      // 获取Bgy.Domain.xml文件路径
                //var viewmodelXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.ViewModel.xml");// 获取Bgy.ViewModel.xml文件路径

                options.IncludeXmlComments(xmlFileFullPath, true); // 添加控制器层注释,true表示显示控制器注释

                //options.IncludeXmlComments(domainXmlPath);       // 添加Domain层注释
                //options.IncludeXmlComments(viewmodelXmlPath);    // 添加ViewModel层注释

                //对action的名称进行排序。
                options.OrderActionsBy(o => o.RelativePath);

                #endregion

                #region 配置授权认证信息

                //添加一个必须的全局安全信息,
                //第一个参数是方案唯一名称:和AddSecurityDefinition方法指定的方案名称标识一致即可:BearerAuth
                //第二个参数是方案的描述:可以是BasicAuthScheme、ApiKeyScheme的实例或OAuth2Scheme
                options.AddSecurityDefinition("BearerAuth", new OpenApiSecurityScheme()
                {
                    Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
                    Name = "Authorization",
                    In = ParameterLocation.Header, //配置jwt默认加在Authorization信息的位置:这里配置的是将jwt信息放在请求头Header中            
                    Type = SecuritySchemeType.Http,//使用Authorize头部                  
                    Scheme = "bearer", //内容为以 bearer开头
                    BearerFormat = "JWT",
                    //Reference= new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                });

                //注册全局认证(所有的接口都可以使用认证)
                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "BearerAuth" //方案名称标识
                            }
                        },
                        new string[] {} //不设权限
                    }
                });

                #endregion

                #region 在Swagger中扩展文件上传按钮
                options.OperationFilter<FileUploadFilter>();
                #endregion
            });
            #endregion
        }

        /// <summary>
        /// 扩展方法:注册及配置Swagger
        /// 用法:在Program.cs文件中进行注册:app.UseSwaggerUIExt();
        /// </summary>
        /// <param name="app"></param>
        public static void UseSwaggerUIExt(this WebApplication app)
        {
            //SwaggerBasicAuthMiddleware:是我自己扩展的一个中间件:目的是需要登陆才能到达Swagger的Index页面中,否则无法进入:可以根据需要去掉这个
            //需要安装:Swashbuckle.AspNetCore包
            //app.UseMiddleware<SwaggerBasicAuthMiddleware>();

            if (app.Environment.IsDevelopment())
            {
                //app.UseSwagger();
                //app.UseSwaggerUI();
            }

            var enviroment = app.Configuration["Swagger:environmentVariables"];
            switch (enviroment)
            {
                case "development":
                    app.UseSwagger();//启用Swagger中间件

                    app.UseSwaggerUI(options =>  //配置版本
                    {
                        typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        {
                            options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        });

                    });

                    break;
                case "testing":
                    app.UseSwagger();
                    app.UseSwaggerUI(options =>
                    {
                        typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        {
                            options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        });
                    });
                    break;
                case "production":
                    break;

            }
        }
    }

    #region 扩展功能
    /// <summary>
    /// 文件上传的扩展:实现在Swagger中上传文件的功能
    /// </summary>
    public class FileUploadFilter : IOperationFilter
    {
        /// <summary>
        /// 文件上传筛选:只有上传文件的方法才添加此功能
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            const string FileUploadContentType = "multipart/form-data";
            if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(FileUploadContentType, StringComparison.InvariantCultureIgnoreCase)))
            {
                return;
            }
            if (context.ApiDescription.ParameterDescriptions[0].Type == typeof(IFormCollection))
            {
                operation.RequestBody = new OpenApiRequestBody
                {
                    Description = "文件上传",
                    Content = new Dictionary<string, OpenApiMediaType>
                    {
                        {
                            FileUploadContentType,new OpenApiMediaType
                            {
                                Schema=new OpenApiSchema
                                {
                                    Type="object",
                                    Required=new HashSet<string>{ "file"},
                                    Properties=new Dictionary<string, OpenApiSchema>
                                    {
                                        {
                                            "file",new OpenApiSchema
                                            {
                                                Type="string",
                                                Format="binary"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                };
            }
        }
    }

    /// <summary>
    /// 如何在ASP.Net Core的生产环境中保护swagger ui,也就是index.html页面。其实swagger是自带禁用的功能的,只需要设置开关即可。
    /// 但是有一些场景,是需要把这些接口进行开放或者导出成文档供第三方进行调用,这个时候却又不想让所有人访问。
    /// 这里介绍一种权限控制访问的方式,用来指定用户使用;
    /// </summary>
    public class SwaggerBasicAuthMiddleware
    {
        private readonly RequestDelegate next;
        /// <summary>
        /// 增加对swagger ui的验证
        /// </summary>
        /// <param name="next"></param>
        public SwaggerBasicAuthMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        /// <summary>
        /// 登陆功能实现
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/swagger"))
            {
                string authHeader = context.Request.Headers["Authorization"];
                if (authHeader != null && authHeader.StartsWith("Basic "))
                {
                    // Get the credentials from request header
                    var header = AuthenticationHeaderValue.Parse(authHeader);
                    var inBytes = Convert.FromBase64String(header.Parameter);
                    var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];

                    //用户身份认证
                    if (username.Equals("admin") && password.Equals("123456"))
                    {
                        await next.Invoke(context).ConfigureAwait(false);
                        return;
                    }
                }
                context.Response.Headers["WWW-Authenticate"] = "Basic";
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
            else
            {
                await next.Invoke(context).ConfigureAwait(false);
            }
        }
    }
    #endregion
}

3、appsettings.json配置文件

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Swagger": {
    "environmentVariables": "development" //:development  :testing  :production
  }
}

4、在Program.cs中注册SwaggerDoc及启用SwaggerUI

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using WebApp.Common.Swagger;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();


#region JWT鉴权授权
var audience = "Audience";
var issuer = "Issuer";
var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  //默认授权机制名称;                                      
         .AddJwtBearer(options =>
         {
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 ValidateIssuer = true,//是否验证Issuer
                 ValidateAudience = true,//是否验证Audience
                 ValidateLifetime = true,//是否验证失效时间
                 ValidateIssuerSigningKey = true,//是否验证SecurityKey
                 ValidAudience = audience,//Audience
                 ValidIssuer = issuer,//Issuer,这两项和前面签发jwt的设置一致  表示谁签发的Token
                 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey))//拿到SecurityKey
             };
         });
#endregion


builder.Services.AddSwaggerGenExt();//SwaggerGen

var app = builder.Build();

app.UseSwaggerUIExt(); //SwaggerUI

app.UseAuthentication();

app.UseAuthorization();

app.MapControllers();

app.Run();

5、Api接口中使用

在接口控制器,或者方法上添加版本(组)标识:[ApiExplorerSettings(GroupName = "v1")]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WebApp.Enums;

namespace WebApp.Controllers
{
    /// <summary>
    /// 测试接口
    /// </summary>
    [ApiController]//[ApiController]能够推断参数的绑定源,就不需要[FromBody][FromForm][FromHeader][FromQuery][FromRoute]....来主动指定接收参数的形式
    [Route("api/[controller]/[action]")]
  
    public class HomeController : ControllerBase
    {
        private readonly ILogger<HomeController> _logger;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }


        /// <summary>
        /// 查询案列1
        /// </summary>
        /// <param name="id">编号</param>
        /// <returns></returns>
        [HttpGet("Abc")] //url地址是:api/WeatherForecast/Get/Abc
        [ApiExplorerSettings(GroupName = "v1")]
        [Authorize]
        public IActionResult Get(int id)
        {
            return Ok(id);

            //返回值:IActionResult

            //return NotFound(); 404

            //return Redirect("/Home/Index");

            //var content = "Hello, World!";
            //return Content(content, "text/plain");

            //var data = new { Name = "John", Age = 30 };
            //return Json(data);

            //var filePath = "/path/to/file.pdf";
            //return File(filePath, "application/pdf", "filename.pdf");

            //byte[] videoBytes = System.IO.File.ReadAllBytes(containerPath);
            //return File(videoBytes, "video/mp4");
        }

        /// <summary>
        /// 查询案列2:路由的伪静态
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [HttpGet("Abc/{name}")] //url地址是:api/WeatherForecast/Get/Abc/lily   :lily是name值, 同时name值是必填的,{name}必须要与action的参数名称一致。这就是路由的伪静态形式
        [ApiExplorerSettings(GroupName = nameof(ApiVersion.v1))]
        [Authorize]
        public IActionResult Get(string name)
        {
            return Ok(name);
        }


        /// <summary>
        /// 客户端登陆
        /// </summary>
        /// <param name="clientid">客户端名称</param>
        /// <param name="password">客户端密码</param>
        /// <returns>返回jwtToken</returns>
        [HttpGet]
        [ApiExplorerSettings(GroupName = nameof(ApiVersion.v2))]
        [Route("api/login")]
        public IActionResult Login(string clientid, string password)
        {
            //这里肯定是需要去连接数据库做数据校验
            if (clientid == "admin" && password == "123456")//应该数据库
            {
                string token = GetJwtToken(clientid);
                return Ok(new { token });
            }
            else
            {
                return Ok("");
            }
        }

        /// <summary>
        /// 获取Token
        /// </summary>
        /// <param name="UserName"></param>
        /// <returns></returns>
        [NonAction]
        public string GetJwtToken(string UserName)
        {
            var issuer = "Issuer";
            var audience = "Audience";

            var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";

            Claim[] claims = new[]
            {
               new Claim(ClaimTypes.Name, UserName)
            };
            SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(securityKey));
            SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims: claims,
                expires: DateTime.Now.AddMinutes(1),//5分钟有效期
                signingCredentials: creds);
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 文件上传
        /// </summary>
        /// <param name="from"></param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult UploadFile(IFormCollection from)
        {
            return new JsonResult(new
            {
                Success = true,
                Message = "上传成功",
                FileName = from.Files.FirstOrDefault()?.FileName

            }) ;     
        }


        /// <summary>
        /// 标记了[NonAction]特性,则不被视为控制器的操作方法
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost(Name = "{id}")]
        [NonAction]
        public string PostTest(int id)
        {
            return id.ToString();
        }
    }
}

6、项目配置生成XML文件

7、效果图

7、注意点:

如果只是单纯只返回token的时候,记得在控制器右上角的Authorize里  先写Bearer+空格+你的token 

第二种:使用插件包

参考文档:.net swagger_51CTO博客_.net core swagger

项目中引入:Asp.Versioning.Mvc.ApiExplorer

项目中引入:Asp.Versioning.Mvc:这个好像可以不用。

1、添加SwaggerExtentsion扩展类,配置注册Swagger

using Asp.Versioning.ApiExplorer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;

namespace WebApp.Common.Swagger
{
    /// <summary>
    /// Swagger扩展
    /// 引入包:Asp.Versioning.Mvc.ApiExplorer
    /// 引入包:Asp.Versioning.Mvc
    /// </summary>
    public static class SwaggerExtension
    {
        /// <summary>
        /// 添加API版本控制的支持
        /// 用法:在Program.cs文件中进行注册:builder.Services.ApiVersionExt();
        /// </summary>
        /// <param name="services"></param>
        public static void ApiVersionExt(this IServiceCollection services)
        {
            //添加API版本支持
            services.AddApiVersioning(option =>
            {
                //是否在响应的header信息中返回API版本信息
                option.ReportApiVersions = true;
                //默认的API版本
                option.DefaultApiVersion = new Asp.Versioning.ApiVersion(1, 0);
                //未指定API版本时,设置API版本为默认的版本
                option.AssumeDefaultVersionWhenUnspecified = true;
            }).AddApiExplorer(options =>//配置API版本信息
            {
                //API版本名的格式:v+版本号
                options.GroupNameFormat = "'v'VVV";
                //未指定API版本时,设置API版本位默认的版本
                options.AssumeDefaultVersionWhenUnspecified = true;
                //在路由模板中,获取或设置一个值,该值指示是否应替换API版本参数
                options.SubstituteApiVersionInUrl = true;
            });
        }

        /// <summary>
        /// 扩展方法:添加Swagger服务
        /// </summary>
        /// <param name="services"></param>
        public static void AddSwaggerGenExt(this IServiceCollection services)
        {
            #region 添加Swagger
            //获取的是当前执行的方法所在的程序文件的名称:即项目名称,例如我的项目名称叫webapp 
            var AssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            services.AddSwaggerGen(options =>
            {
                #region 自定义支持版本展示
                var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
                //这个provider.ApiVersionDescriptions就是打在控制器或者方法上的 [ApiVersion("1.0")]这个特性的集合
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    options.SwaggerDoc(description.GroupName, new OpenApiInfo
                    {

                        Version = description.ApiVersion.ToString(),
                        Title = "凤凰网管理系统",
                        Description = $"凤凰网接口服务    版本: {description.ApiVersion}",
                        Contact = new OpenApiContact
                        {
                            Name = "潇湘夜雨",
                            Email = "123@qq.com"
                        }
                    });
                    //在Swagger文档显示的API地址中将版本信息参数替换位实际的版本号
                    options.DocInclusionPredicate((version, apiDescription) =>
                    {
                        if (!version.Equals(apiDescription.GroupName))
                        {
                            return false;
                        }
                        return true;
                    });
                    //参数使用驼峰命名方式
                    options.DescribeAllParametersInCamelCase();
                    //取消API文档需要输入的版本信息
                    options.OperationFilter<RemoveVersionFromParameter>();
                }
                #endregion
                #region 配置注释文档

                // 获取当前项目的 XML文档文件路径:比如我的项目名称叫WebApp,那么它默认的 XML文档文件路径就是当前项目下的 WebApp.xml
                var xmlFile = $"{AssemblyName}.xml";
                var xmlFileFullPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

                //var domainXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.Domain.xml");      // 获取Bgy.Domain.xml文件路径
                //var viewmodelXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.ViewModel.xml");// 获取Bgy.ViewModel.xml文件路径

                options.IncludeXmlComments(xmlFileFullPath, true); // 添加控制器层注释,true表示显示控制器注释

                //options.IncludeXmlComments(domainXmlPath);       // 添加Domain层注释
                //options.IncludeXmlComments(viewmodelXmlPath);    // 添加ViewModel层注释

                //对action的名称进行排序。
                options.OrderActionsBy(o => o.RelativePath);
                //TagActionsBy用于获取一个接口所在的标签分组,默认的接口标签分组是控制器名,也就是接口被分在它所属的控制器下面,我们可以改成按请求方法进行分组:与上面的options.OrderActionsBy(o => o.RelativePath);代码效果等同
                //options.TagActionsBy(apiDescription => new string[] { apiDescription.HttpMethod });
                //options.OrderActionsBy(apiDescription => apiDescription.RelativePath.Length.ToString());//根据路由的长度来排序

                #endregion

                #region 配置授权认证信息


                //AddSecurityDefinition用于声明一个安全认证,注意,只是声明,并未指定接口必须要使用认证,比如声明JwtBearer认证方式:
                //第一个参数是方案唯一名称:和AddSecurityDefinition方法指定的方案名称标识一致即可:JwtBearer
                //第二个参数是方案的描述:可以是BasicAuthScheme、ApiKeyScheme的实例或OAuth2Scheme
                options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
                {
                    Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
                    Name = "Authorization",        //jwt默认的参数名称
                    In = ParameterLocation.Header, //配置jwt默认加在Authorization信息的位置:这里配置的是将jwt信息放在请求头Header中            
                    Type = SecuritySchemeType.Http,//使用Authorize头部                  
                    Scheme = "bearer",             //内容为以 bearer开头
                    BearerFormat = "JWT",
                });


                //注册全局认证(所有的接口都可以使用认证)
                //AddSecurityRequirement是将声明的认证作用于所有接口(AddSecurityRequirement好像可以声明和引用一起实现),比如将上面的JwtBearer认证作用于所有接口: 
                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "JwtBearer" //方案名称标识
                            }
                        },
                        new string[] {} //不设权限
                    }
                });

                #endregion

                #region 在Swagger中扩展文件上传按钮
                options.OperationFilter<FileUploadFilter>();
                #endregion
            });
            #endregion
        }

        /// <summary>
        /// 扩展方法:配置及注册Swagger服务
        /// 用法:在Program.cs文件中进行注册:app.UseSwaggerUIExt();
        /// </summary>
        /// <param name="app"></param>
        public static void UseSwaggerUIExt(this WebApplication app)
        {
            //SwaggerBasicAuthMiddleware:是我自己扩展的一个中间件:目的是需要登陆才能到达Swagger的Index页面中,否则无法进入:可以根据需要去掉这个
            //需要安装:Swashbuckle.AspNetCore包
            //app.UseMiddleware<SwaggerBasicAuthMiddleware>();

            if (app.Environment.IsDevelopment())
            {
                //app.UseSwagger();
                //app.UseSwaggerUI();
            }

            var enviroment = app.Configuration["Swagger:environmentVariables"];
            switch (enviroment)
            {
                case "development":
                    app.UseSwagger();//启用Swagger中间件

                    app.UseSwaggerUI(options =>  //配置版本
                    {
                        //typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        //{
                        //    options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        //});
                        #region 调用第三方程序包支持版本控制
                        var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
                        foreach (var description in provider.ApiVersionDescriptions.Reverse())
                        {
                            options.SwaggerEndpoint(
                                $"/swagger/{description.GroupName}/swagger.json",
                                $"凤凰网接口服务    版本: {description.GroupName.ToUpperInvariant()}"
                                );
                        }
                        #endregion

                    });

                    break;
                case "testing":
                    app.UseSwagger();
                    app.UseSwaggerUI(options =>
                    {
                        //typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
                        //{
                        //    options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
                        //});
                    });
                    break;
                case "production":
                    break;

            }
        }
    }

    #region 扩展功能

    /// <summary>
    /// 删除Swagger页面中要求录入的版本号
    /// 添加了Swagger的版本号支持默认回在Swagger中有一个录入版本号的文本框,我们的目的是不在Swagger中录入版本号,所以我们将这个录入版本号的文本框删除
    /// </summary>
    public class RemoveVersionFromParameter : IOperationFilter
    {
        /// <summary>
        /// 扩展
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var versionParameter = operation.Parameters.FirstOrDefault(p => p.Name == "version");
            if (versionParameter != null)
            {
                //如果存在version这个参数,则删除
                operation.Parameters.Remove(versionParameter);
            }
        }
    }


    /// <summary>
    /// 文件上传的扩展:实现在Swagger中上传文件的功能
    /// </summary>
    public class FileUploadFilter : IOperationFilter
    {
        /// <summary>
        /// 文件上传筛选:只有上传文件的方法才添加此功能
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            const string FileUploadContentType = "multipart/form-data";
            if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(FileUploadContentType, StringComparison.InvariantCultureIgnoreCase)))
            {
                return;
            }
            if (context.ApiDescription.ParameterDescriptions[0].Type == typeof(IFormCollection))
            {
                operation.RequestBody = new OpenApiRequestBody
                {
                    Description = "文件上传",
                    Content = new Dictionary<string, OpenApiMediaType>
                    {
                        {
                            FileUploadContentType,new OpenApiMediaType
                            {
                                Schema=new OpenApiSchema
                                {
                                    Type="object",
                                    Required=new HashSet<string>{ "file"},
                                    Properties=new Dictionary<string, OpenApiSchema>
                                    {
                                        {
                                            "file",new OpenApiSchema
                                            {
                                                Type="string",
                                                Format="binary"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                };
            }
        }
    }

    /// <summary>
    /// 如何在ASP.Net Core的生产环境中保护swagger ui,也就是index.html页面。其实swagger是自带禁用的功能的,只需要设置开关即可。
    /// 但是有一些场景,是需要把这些接口进行开放或者导出成文档供第三方进行调用,这个时候却又不想让所有人访问。
    /// 这里介绍一种权限控制访问的方式,用来指定用户使用;
    /// </summary>
    public class SwaggerBasicAuthMiddleware
    {
        private readonly RequestDelegate next;
        /// <summary>
        /// 增加对swagger ui的验证
        /// </summary>
        /// <param name="next"></param>
        public SwaggerBasicAuthMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        /// <summary>
        /// 登陆功能实现
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/swagger"))
            {
                string authHeader = context.Request.Headers["Authorization"];
                if (authHeader != null && authHeader.StartsWith("Basic "))
                {
                    var header = AuthenticationHeaderValue.Parse(authHeader);
                    var inBytes = Convert.FromBase64String(header.Parameter);
                    var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];

                    //用户身份认证
                    if (username.Equals("admin") && password.Equals("123456"))
                    {
                        await next.Invoke(context).ConfigureAwait(false);
                        return;
                    }
                }
                context.Response.Headers["WWW-Authenticate"] = "Basic";
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
            else
            {
                await next.Invoke(context).ConfigureAwait(false);
            }
        }
    }
    #endregion
}

2、在Program.cs中注册SwaggerDoc及启用SwaggerUI

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using WebApp.Common.Swagger;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();


builder.Services.ApiVersionExt(); //api版本支持

builder.Services.AddSwaggerGenExt();//SwaggerGen



var app = builder.Build();

app.UseSwaggerUIExt(); //SwaggerUI

app.UseAuthentication();

app.UseAuthorization();

app.MapControllers();

app.Run();

3、Api接口中使用

v1版本Home控制器

using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;

namespace WebApp.Controllers.v1
{
    /// <summary>
    /// 测试接口:注意如果v1,和v2版本的控制器名称一致,比如都叫HomeController,需要在Controllers文件夹下新建一个v2的文件夹来存放v2版本的HomeController控制器,反正不同版本的名称相同的控制器要放在不同的文件夹下,文件夹可以取名 v1 v2,用来存放不同版本名称相同的控制器
    /// </summary>
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/[controller]/v{version:apiVersion}")] //路由建议写成这样,这个apiVersion是版本号,即[ApiVersion("1.0")]中的1.0;//如果不把版本号写在URL里,也可以用请求参数传递,比如 /api/home?api-version=1.0 这些可以在 services.AddApiVersioning()中添加 ApiVersionReader 属性配置
    public class HomeController : ControllerBase
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public HomeController()
        {

        }

        /// <summary>
        /// 测试方法
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Get(string name)
        {
            return Ok(name);
        }
    }
}

v2版本的Home控制器

using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;

namespace WebApp.Controllers.v2
{
    /// <summary>
    /// 测试接口:注意如果v1,和v2版本的控制器名称一致,比如都叫HomeController,需要在Controllers文件夹下新建一个v2的文件夹来存放v2版本的HomeController控制器,反正不同版本的名称相同的控制器要放在不同的文件夹下,文件夹可以取名 v1 v2,用来存放不同版本名称相同的控制器
    /// </summary>
    [ApiController]
    [ApiVersion("2.0")]
    [Route("api/[controller]/v{version:apiVersion}")] //路由建议写成这样,这个apiVersion是版本号,即[ApiVersion("1.0")]中的2.0 //如果不把版本号写在URL里,也可以用请求参数传递,比如 /api/home?api-version=2.0 这些可以在 services.AddApiVersioning()中添加ApiVersionReader 属性配置
    public class HomeController : ControllerBase
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public HomeController()
        {

        }

        /// <summary>
        /// 测试方法
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Get(string name)
        {
            return Ok(name);
        }
    }
}

4、效果图

5、Swagger版本号格式化:API Version Format Strings

 我们在上文中用到了:options.GroupNameFormat = "'v'VVV" 

本文中我使用的是 'v'VVV 的格式

Format Specifier

(格式指示符)

Description(描述)Examples(案列)
FFull API version as [group version][.major[.minor]][-status]2017-05-01.1-RC -> 2017-05-01.1-RC
FFFull API version with optional minor version as [group version][.major[.minor,0]][-status]2017-05-01.1-RC -> 2017-05-01.1.0-RC
GGroup version as yyyy-MM-dd2017-05-01.1-RC -> 2017-05-01
GGGroup version as yyyy-MM-dd with status2017-05-01.1-RC -> 2017-05-01-RC
yGroup version year from 0 to 992001-05-01.1-RC -> 1
yyGroup version year from 00 to 992001-05-01.1-RC -> 01
yyyGroup version year with a minimum of three digits2017-05-01.1-RC -> 017
yyyyGroup version year as a four-digit number2017-05-01.1-RC -> 2017
MGroup version month from 1 through 122001-05-01.1-RC -> 5
MMGroup version month from 01 through 122001-05-01.1-RC -> 05
MMMGroup version abbreviated name of the month2001-06-01.1-RC -> Jun
MMMMGroup version full name of the month2001-06-01.1-RC -> June
dGroup version day of the month, from 1 through 312001-05-01.1-RC -> 1
ddGroup version day of the month, from 01 through 312001-05-01.1-RC -> 01
dddGroup version abbreviated name of the day of the week2001-05-01.1-RC -> Mon
ddddGroup version full name of the day of the week2001-05-01.1-RC -> Monday
vMinor version2001-05-01.1-RC -> 1 1.1 -> 1
VMajor version1.0-RC -> 1 2.0 -> 2
VVMajor and minor version1-RC -> 1 1.1-RC -> 1.1 1.1 -> 1.1
VVVMajor, optional minor version, and status1-RC -> 1-RC 1.1 -> 1.1
VVVVMajor, minor version, and status1-RC -> 1.0-RC 1.1 -> 1.1 1 -> 1.0
SStatus1.0-Beta -> Beta
pPadded minor version with default of two digits1.1 -> 01 1 -> 00
p[n]Padded minor version with N digitsp2: 1.1 -> 01 p3: 1.1 -> 001
PPadded major version with default of two digits2.1 -> 02 2 -> 02
P[n]Padded major version with N digitsP2: 2.1 -> 02 P3: 2.1 -> 002
PPPadded major and minor version with a default of two digits2.1 -> 02.01 2 -> 02.00
PPPPadded major, optional minor version, and status with a default of two digits1-RC -> 01-RC 1.1-RC -> 01.01-RC
PPPPPadded major, minor version, and status with a default of two digits1-RC -> 01.00-RC 1.1-RC -> 01.01-RC

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值