• .net swagger


    注入

    services.AddSwaggerGen(options =>
        {
            //使用options注入服务
        });   
    

    SwaggerDoc

    SwaggerDoc主要用来声明一个文档,上面的例子中声明了一个名称为v1的接口文档,可声明多个接口文档,比如按开发版本进行声明:  

    options.SwaggerDoc("v1", new OpenApiInfo()
        {
            Version = "v0.0.1",
            Title = "项目v0.0.1",
            Description = $"接口文档说明v0.0.1",
            Contact = new OpenApiContact()
            {
                Name = "zhangsan",
                Email = "xxx@qq.com",
                Url = null
            }
        });
    
        options.SwaggerDoc("v2", new OpenApiInfo()
        {
            Version = "v0.0.2",
            Title = "项目v0.0.2",
            Description = $"接口文档说明v0.0.2",
            Contact = new OpenApiContact()
            {
                Name = "lisi",
                Email = "xxxx@qq.com",
                Url = null
            }
        });
    

    IncludeXmlComments

    IncludeXmlComments是用于加载注释文件,Swashbuckle会从注释文件中去获取接口的注解,接口参数说明以及接口返回的参数说明等信息,这个在上面的一般用法中已经介绍了,这里不再重复说明

    IgnoreObsoleteActions

    IgnoreObsoleteActions表示过滤掉ObsoleteAttribute属性声明的接口,也就是说不会在SwaggerUI中显示接口了,ObsoleteAttribute修饰的接口表示接口已过期,尽可能不要再使用。

    方法调用等价于:options.SwaggerGeneratorOptions.IgnoreObsoleteActions = true;

    IgnoreObsoleteProperties

    IgnoreObsoleteProperties的作用类似于IgnoreObsoleteActions,只不过IgnoreObsoleteActions是作用于接口,而IgnoreObsoleteProperties作用于接口的请求实体和响应实体参数中的属性。

    方法调用等价于:  

    options.SchemaGeneratorOptions.IgnoreObsoleteProperties = true;

    OrderActionsBy

    OrderActionsBy用于同一组接口(可以理解为同一控制器下的接口)的排序,默认情况下,一般都是按接口所在类的位置进行排序(源码中是按控制器名称排序,但是同一个控制器中的接口是一样的)。

      比如上面的例子中,我们可以修改成按接口路由长度排序:  

    options.OrderActionsBy(apiDescription => apiDescription.RelativePath.Length.ToString());
    

    CustomSchemaIds

    CustomSchemaIds方法用于自定义SchemaId,Swashbuckle中的每个Schema都有唯一的Id,框架会使用这个Id匹配引用类型,因此这个Id不能重复。

      默认情况下,这个Id是根据类名得到的(不包含命名空间),因此,当我们有两个相同名称的类时,Swashbuckle就会报错:  

    System.InvalidOperationException: Can't use schemaId "$XXXXX" for type "$XXXX.XXXX". The same schemaId is already used for type "$XXXX.XXXX.XXXX"
    

      就是类似上面的异常,一般时候我们都得去改类名,有点不爽,这时就可以使用这个方法自己自定义实现SchemaId的获取,比如,我们自定义实现使用类名的全限定名(包含命名空间)来生成SchemaId,上面的异常就没有了:   

    options.CustomSchemaIds(CustomSchemaIdSelector);
    
    string CustomSchemaIdSelector(Type modelType)
    {
        if (!modelType.IsConstructedGenericType) return modelType.FullName.Replace("[]", "Array");
    
        var prefix = modelType.GetGenericArguments()
            .Select(genericArg => CustomSchemaIdSelector(genericArg))
            .Aggregate((previous, current) => previous + current);
    
        return prefix + modelType.FullName.Split('`').First();
    }
    

    TagActionsBy

    Tag是标签组,也就是将接口做分类的一个概念。

      TagActionsBy用于获取一个接口所在的标签分组,默认的接口标签分组是控制器名,也就是接口被分在它所属的控制器下面,我们可以改成按请求方法进行分组  

    options.TagActionsBy(apiDescription => new string[] { apiDescription.HttpMethod});
    

    AddSecurityDefinition

    AddSecurityDefinition用于声明一个安全认证,注意,只是声明,并未指定接口必须要使用认证,比如声明JwtBearer认证方式:  

     //定义JwtBearer认证方式一
        options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
        {
            Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer)",
            Name = "Authorization",//jwt默认的参数名称
            In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
            Type = SecuritySchemeType.Http,
            Scheme = "bearer"
        });
    

    AddSecurityRequirement

    AddSecurityDefinition仅仅是声明已一个认证,不一定要对接口用,而AddSecurityRequirement是将声明的认证作用于所有接口(AddSecurityRequirement好像可以声明和引用一起实现),比如将上面的JwtBearer认证作用于所有接口:  

    //声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
    var scheme = new OpenApiSecurityScheme()
    {
        Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
    };
    //注册全局认证(所有的接口都可以使用认证)
    options.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        [scheme] = new string[0]
    });
    

      运行后,发现所有接口后面多了一个锁,表明此接口需要认证信息:

    DocumentFilter

    DocumentFilter是文档过滤器,它是在获取swagger文档接口,返回结果前调用,也就是请求swagger.json时调用,它允许我们对即将返回的swagger文档信息做调整,比如上面的例子中添加的全局认证方式和AddSecurityRequirement添加的效果是一样的:

     public class MyDocumentFilter : IDocumentFilter
        {
            public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
            {
                //声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
                var scheme = new OpenApiSecurityScheme()
                {
                    Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
                };
                //注册全局认证(所有的接口都可以使用认证)
                swaggerDoc.SecurityRequirements.Add(new OpenApiSecurityRequirement()
                {
                    [scheme] = new string[0]
                });
            }
        }
    

    然后使用DocumentFilter方法添加过滤器:  

    options.DocumentFilter<MyDocumentFilter>();
    

      DocumentFilter方法需要提供一个实现了IDocumentFilter接口的Apply方法的类型和它实例化时所需要的的参数,而IDocumentFilter的Apply方法提供了OpenApiDocument和DocumentFilterContext两个参数,DocumentFilterContext参数则包含了当前文件接口方法的信息,比如调用的接口的Action方法和Action的描述(如路由等)。而OpenApiDocument即包含当前请求的接口文档信息,它包含的属性全部都是全局性的, 这样我们可以像上面添加认证一样去添加全局配置,比如,如果不使用AddServer方法,我们可以使用DocumentFilter去添加:  

     public class MyDocumentFilter : IDocumentFilter
        {
            public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
            {
                swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://localhost:90", Description = "地址1" });
                swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90", Description = "地址2" });
                //192.168.28.213是我本地IP
                swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90", Description = "地址3" });
            }
        }
    

    再比如,上面我们对接口进行了swagger文档分类使用的是ApiExplorerSettingsAttribute,如果不想对每个接口使用ApiExplorerSettingsAttribute,我们可以使用DocumentFilter来实现,先创建一个类实现IDocumentFilter接口:

     public class GroupNameDocumentFilter : IDocumentFilter
        {
            string documentName;
            string[] actions;
    
            public GroupNameDocumentFilter(string documentName, params string[] actions)
            {
                this.documentName = documentName;
                this.actions = actions;
            }
    
            public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
            {
                foreach (var apiDescription in context.ApiDescriptions)
                {
                    if (actions.Contains(apiDescription.ActionDescriptor.RouteValues["action"]))
                    {
                        apiDescription.GroupName = documentName;
                    }
                }
            }
        }
       
    //然后使用DocumentFilter添加过滤器: 
    
        //All和Get接口属于文档v1
        options.DocumentFilter<GroupNameDocumentFilter>(new object[] { "v1", new string[] { nameof(HomeController.Get) } });
        //All和Post接口属于v2
        options.DocumentFilter<GroupNameDocumentFilter>(new object[] { "v2", new string[] { nameof(HomeController.Post) } });
    

    OperationFilter

    Operation可以简单的理解为一个操作,因为swagger是根据项目中的接口,自动生成接口文档,就自然需要对每个接口进行解析,接口路由是什么,接口需要什么参数,接口返回什么数据等等,而对每个接口的解析就可以视为一个Operation。

      OperationFilter是操作过滤器,这个方法需要一个实现类IOperationFilter接口的类型,而它的第二个参数arguments是这个类型实例化时传入的参数。

      OperationFilter允许我们对已经生成的接口进行修改,比如可以添加参数,修改参数类型等等。

      需要注意的是,OperationFilter在获取swagger文档接口时调用,也就是请求swagger.json时调用,而且只对属于当前请求接口文档的接口进行过滤调用。  

      比如我们有一个Operation过滤器:

     public class MyOperationFilter : IOperationFilter
        {
            string documentName;
    
            public MyOperationFilter(string documentName)
            {
                this.documentName = documentName;
            }
    
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                //过滤处理
            }
        }
    

    接着调用SwaggerGenOptions的OperationFilter方法添加  

    options.OperationFilter<MyOperationFilter>(new object[] { "v1" });
    

    上面的过滤器实例化需要一个参数documentName,所以在OperationFilter方法中有一个参数。

      这个接口只会对当前请求的接口文档进行调用,也就是说,如果我们请求的是swagger文档v1,也就是请求/swagger/v1/swagger.json时,这个过滤器会对All方法和Get方法执行,如果请求的是swagger文档v2,也就是请求/swagger/v2/swagger.json时,这个过滤器会对All方法和Post方法进行调用。自定义的OperationFilter需要实现IOperationFilter的Apply接口方法,而Apply方法有两个参数:OpenApiOperation和OperationFilterContext,同样的,OpenApiOperation包含了和当前接口相关的信息,比如认证情况,所属的标签,还可以自定义的自己的Servers。而OperationFilterContext则包换了接口方法的的相关引用。

      OperationFilter是用的比较多的方法了,比如上面的全局认证,因为直接调用AddSecurityRequirement添加的是全局认证,但是项目中可能部分接口不需要认证,这时我们就可以写一个OperationFilter对每一个接口进行判断了: 

     public class ResponsesOperationFilter : IOperationFilter
        {
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
                    .Union(context.MethodInfo.GetCustomAttributes(true))
                    .OfType<AuthorizeAttribute>();
    
                var list = new List<OpenApiSecurityRequirement>();
                if (authAttributes.Any() && !context.MethodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any())
                {
                    operation.Responses["401"] = new OpenApiResponse { Description = "Unauthorized" };
                    //operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
    
                    //声明一个Scheme,注意下面的Id要和AddSecurityDefinition中的参数name一致
                    var scheme = new OpenApiSecurityScheme()
                    {
                        Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
                    };
                    //注册全局认证(所有的接口都可以使用认证)
                    operation.Security = new List<OpenApiSecurityRequirement>(){new OpenApiSecurityRequirement()
                    {
                        [scheme] = new string[0]
                    }};
                }
            }
        }
    

    然后使用OperationFilter添加这个过滤器:  

    options.OperationFilter<ResponsesOperationFilter>();
    

    swagger统一返回值

    public class MyOperationFilter : IOperationFilter
        {
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                foreach (var key in operation.Responses.Keys)
                {
                    var content = operation.Responses[key].Content;
                    foreach (var mediaTypeKey in content.Keys)
                    {
                        var mediaType = content[mediaTypeKey];
                        var schema = new OpenApiSchema();
                        schema.Type = "object";
                        schema.Properties = new Dictionary<string, OpenApiSchema>()
                        {
                            ["code"] = new OpenApiSchema() { Type = "integer" },
                            ["message"] = new OpenApiSchema() { Type = "string" },
                            ["error"] = new OpenApiSchema()
                            {
                                Type = "object",
                                Properties = new Dictionary<string, OpenApiSchema>()
                                {
                                    ["message"] = new OpenApiSchema() { Type = "string" },
                                    ["stackTrace"] = new OpenApiSchema() { Type = "string" }
                                }
                            },
                            ["result"] = mediaType.Schema
                        };
                        mediaType.Schema = schema;
                    }
                }
            }
        }
    

    注入
    options.OperationFilter();

    RequestBodyFilter

      RequestBody理所当然的就是请求体了,一般指的就是Post请求,RequestBodyFilter就是允许我们对请求体的信息作出调整,同样的,它是在获取Swagger.json文档时调用,而且只对那些有请求体的接口才会执行。

      RequestBodyFilter的用法类似DocumentFilter和OperationFilter,一般也不会去修改请求体的默认行为,因为它可能导致请求失败,所以一般不常用,这里就不介绍了

    ParameterFilter

      Parameter指的是接口的参数,而ParameterFilter当然就是允许我们对参数的结构信息作出调整了,同样的,它是在获取Swagger.json文档时调用,而且只对那些参数的接口才会执行。

    SchemaFilter

      Schema指的是结构,一般指的是接口请求参数和响应返回的参数结构,比如我们想将所有的int类型换成string类型,对特定类型进行处理 

    //实现调整指定类型的描述信息
    public class MySchemaFilter : ISchemaFilter
        {
            public void Apply(OpenApiSchema schema, SchemaFilterContext context)
            {
                if (context.Type == typeof(int))
                {
                    schema.Type = "string";
                  schema.Description += "test";
                }
            }
        }
    

    注入
    services.AddSwaggerGen(options =>
    {
    ...

        options.SchemaFilter<MySchemaFilter>();
    });
    

    swagger默认值

    [DefaultValue(1)]
    public int PageIndex{get;set;};

    参考

  • 相关阅读:
    codefoeces problem 671D——贪心+启发式合并+平衡树
    bzoj 1598: [Usaco2008 Mar]牛跑步
    bzoj 1050: [HAOI2006]旅行comf&&【codevs1001】
    codefoeces 671 problem D
    利用FFMPEG以及EasyRTMP实现读取H.264文件推RTMP视频流的两种方式
    视频流拉转推工具对比:EasyRTSPLive和FFMPEG拉转推效果对比
    TSINGSEE青犀视频Webrtc实时通信的构建流程:PeerConnection对等通信的实现方式
    TSINGSEE青犀视频云边端架构产品编译Intel Media SDK 编译报错error"SSE4.1 instruction set not enabled"
    H.265编码视频在web网页实现无插件播放,应该通过软解码还是硬解码?
    【案例分析】EasyDSS+EasyCVR融合共享解决方案
  • 原文地址:https://www.cnblogs.com/ives/p/16087098.html
Copyright © 2020-2023  润新知