首页 > 其他分享 >.NET9 - Swagger平替Scalar详解(四)

.NET9 - Swagger平替Scalar详解(四)

时间:2024-11-26 22:10:46浏览次数:11  
标签:平替 Description 枚举 接口 Scalar var new Swagger public

书接上回,上一章介绍了Swagger代替品Scalar,在使用中遇到不少问题,今天单独分享一下之前Swagger中常用的功能如何在Scalar中使用。

下面我们将围绕文档版本说明、接口分类、接口描述、参数描述、枚举类型、文件上传、JWT认证等方面详细讲解。

01、版本说明

我们先来看看默认添加后是什么样子的。

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddControllers();
    builder.Services.AddOpenApi();
    var app = builder.Build();
    app.MapScalarApiReference();
    app.MapOpenApi();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
}

效果如下:

我们可以直接修改builder.Services.AddOpenApi()这行代码,修改这块描述,代码如下:

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, cancellationToken) =>
    {
        document.Info = new()
        {
            Title = "订单微服务",
            Version = "v1",
            Description = "订单相关接口"
        };
        return Task.CompletedTask;
    });
});

我们再来看看效果。

02、接口分类

通过上图可以看到菜单左侧排列着所有接口,现在我们可以通过Tags特性对接口进行分类,如下图我们把增删改查4个方法分为幂等接口和非幂等接口两类,如下图:

然后我们看看效果,如下图:

03、接口描述

之前使用Swagger我们都是通过生成的注释XML来生成相关接口描述,现在则是通过编码的方式设置元数据来生成相关描述。

可以通过EndpointSummary设置接口摘要,摘要不设置默认为接口url,通过EndpointDescription设置接口描述,代码如下:

//获取
[HttpGet(Name = "")]
[Tags("幂等接口")]
[EndpointDescription("获取订单列表")]
public IEnumerable<Order> Get()
{
    return null;
}
//删除
[HttpDelete(Name = "{id}")]
[Tags("幂等接口")]
[EndpointSummary("删除订单")]
[EndpointDescription("根据订单id,删除相应订单")]
public bool Delete(string id)
{
    return true;
}

运行效果如下:

04、参数描述

同样可以通过Description特性来设置参数的描述,并且此特性可以直接作用于接口中参数之前,同时也支持作用于属性上,可以看看下面示例代码。

public class Order
{
    [property: Description("创建日期")]
    public DateOnly Date { get; set; }
    [property: Required]
    [property: DefaultValue(120)]
    [property: Description("订单价格")]
    public int Price { get; set; }
    [property: Description("订单折扣价格")]
    public int PriceF => (int)(Price * 0.5556);
    [property: Description("商品名称")]
    public string? Name { get; set; }
}
[HttpPut(Name = "{id}")]
[Tags("非幂等接口")]
public bool Put([Description("订单Id")] string id, Order order)
{
    return true;
}

效果如下图:

从上图可以发现除了描述还有默认值、必填项、可空等字样,这些是通过其他元数据设置的,对于属性还有以下元数据可以进行设置。

05、枚举类型

对于枚举类型,我们正常关注两个东西,其一为枚举项以int类型展示还是以字符串展示,其二为枚举项显示描述信息。

关于第一点比较简单只要对枚举类型使用JsonStringEnumConverter即可,代码如下:

[JsonConverter(typeof(JsonStringEnumConverter<OrderStatus>))]
public enum OrderStatus
{
    [Description("等待处理")]
    Pending = 1,
    [Description("处理中")]
    Processing = 2,
    [Description("已发货")]
    Shipped = 3,
    [Description("已送达")]
    Delivered = 4,
}

效果如下:

通过上图也可以引发关于第二点的需求,如何对每个枚举项添加描述信息。

要达到这个目标需要做两件事,其一给每个枚举项通过Description添加元数据定义,其二我们要修改文档的数据结构Schema。

修改builder.Services.AddOpenApi(),通过AddSchemaTransformer方法修改文档数据结构,代码如下:

options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
    //找出枚举类型
    if (context.JsonTypeInfo.Type.BaseType == typeof(Enum))
    {
        var list = new List<IOpenApiAny>();
        //获取枚举项
        foreach (var enumValue in schema.Enum.OfType<OpenApiString>())
        {
            //把枚举项转为枚举类型
            if (Enum.TryParse(context.JsonTypeInfo.Type, enumValue.Value, out var result))
            {
                //通过枚举扩展方法获取枚举描述
                var description = ((Enum)result).ToDescription();
                //重新组织枚举值展示结构
                list.Add(new OpenApiString($"{enumValue.Value} - {description}"));
            }
            else
            {
                list.Add(enumValue);
            }
        }
        schema.Enum = list;
    }
    return Task.CompletedTask;
});

我们再来看看结果。

但是这也带来了一个问题,就是参数的默认值也是添加描述的格式,显然这样的数据格式作为参数肯定是错误的,因此我们需要自己注意,如下图。目前我也没有发现更好的方式即可以把每项枚举描述加上,又不影响参数默认值,有解决方案的希望可以不吝赐教。

06、文件上传

下面我们来看看文件上传怎么用,直接上代码:

[HttpPost("upload/image")]
[EndpointDescription("图片上传接口")]
[DisableRequestSizeLimit]
public bool UploadImgageAsync(IFormFile file)
{
    return true;
}

然后我们测试一下效果。

首先我们可以看到请求示例中相关信息,这个相当于告诉我们后面要怎么选择文件上传,我们继续点击Test Request。

首先请求体需要选择multipart/form-data,上图请求示例中已经给出提示。

然后设置key为file,上图请求示例中已经给出提示,然后点击File上传图片,最后点击Send即可。

07、JWT认证

最后我们来看看如何使用JWT认证,

首先我们需要注入AddAuthentication及AddJwtBearer,具体代码如下:

public class JwtSettingOption
{
    //这个字符数量有要求,不能随便写,否则会报错
    public static string Secret { get; set; } = "123456789qwertyuiopasdfghjklzxcb";
    public static string Issuer { get; set; } = "asdfghjkkl";
    public static string Audience { get; set; } = "zxcvbnm";
    public static int Expires { get; set; } = 120;
    public static string RefreshAudience { get; set; } = "zxcvbnm.2024.refresh";
    public static int RefreshExpires { get; set; } = 10080;
}
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    //取出私钥
    var secretByte = Encoding.UTF8.GetBytes(JwtSettingOption.Secret);
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        //验证发布者
        ValidateIssuer = true,
        ValidIssuer = JwtSettingOption.Issuer,
        //验证接收者
        ValidateAudience = true,
        ValidAudiences = new List<string> { JwtSettingOption.Audience, JwtSettingOption.Audience },
        //验证是否过期
        ValidateLifetime = true,
        //验证私钥
        IssuerSigningKey = new SymmetricSecurityKey(secretByte),
        ClockSkew = TimeSpan.FromHours(1), //过期时间容错值,解决服务器端时间不同步问题(秒)
        RequireExpirationTime = true,
    };
});

然后我们需要继续修改builder.Services.AddOpenApi()这行代码,在里面加上如下代码:

其中BearerSecuritySchemeTransformer实现如下:

public sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
    {
        var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
        if (authenticationSchemes.Any(authScheme => authScheme.Name == JwtBearerDefaults.AuthenticationScheme))
        {
            var requirements = new Dictionary<string, OpenApiSecurityScheme>
            {
                [JwtBearerDefaults.AuthenticationScheme] = new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = JwtBearerDefaults.AuthenticationScheme.ToLower(),
                    In = ParameterLocation.Header,
                    BearerFormat = "Json Web Token"
                }
            };
            document.Components ??= new OpenApiComponents();
            document.Components.SecuritySchemes = requirements;
            foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
            {
                operation.Value.Security.Add(new OpenApiSecurityRequirement
                {
                    [new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Id = JwtBearerDefaults.AuthenticationScheme,
                            Type = ReferenceType.SecurityScheme
                        }
                    }] = Array.Empty<string>()
                });
            }
        }
    }
}

下面就可以通过[Authorize]开启接口认证,并实现一个登录接口获取token用来测试。

[HttpPost("login")]
[EndpointDescription("登录成功后生成token")]
[AllowAnonymous]
public string  Login()
{
    //登录成功返回一个token
    // 1.定义需要使用到的Claims
    var claims = new[] { new Claim("UserId", "test") };
    // 2.从 appsettings.json 中读取SecretKey
    var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettingOption.Secret));
    // 3.选择加密算法
    var algorithm = SecurityAlgorithms.HmacSha256;
    // 4.生成Credentials
    var signingCredentials = new SigningCredentials(secretKey, algorithm);
    var now = DateTime.Now;
    var expires = now.AddMinutes(JwtSettingOption.Expires);
    // 5.根据以上,生成token
    var jwtSecurityToken = new JwtSecurityToken(
        JwtSettingOption.Issuer,         //Issuer
        JwtSettingOption.Audience,       //Audience
        claims,                          //Claims,
        now,                             //notBefore
        expires,                         //expires
        signingCredentials               //Credentials
    );
    // 6.将token变为string
    var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
    return token;
}

下面我们先用登录接口获取一个token。

我们先用token调用接口,可以发现返回401。

然后我们把上面获取的token放进去,请求成功。

在这个过程中有可能会遇到一种情况:Auth Type后面的下拉框不可选,如下图。

可能因以下原因导致,缺少[builder.Services.AddAuthentication().AddJwtBearer();]或[options.AddDocumentTransformer();]任意一行代码。

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner

标签:平替,Description,枚举,接口,Scalar,var,new,Swagger,public
From: https://www.cnblogs.com/hugogoos/p/18571088

相关文章

  • swagger学习
    swagger 支持基于API自动生成接口文档,接口文档始终与API保持同步,一、引入依赖:<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>3.0.0</version></dependency><dependency>......
  • 04 springboot-工程搭建案例(多环境部署,数据源, Swagger, 国际化,工具类)
    项目搭建模板(多环境切换)springboot系列,最近持续更新中,如需要请关注如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。文章目录1项目截图2pom.xml3多环境配......
  • 使用博查Web Search API获取搜索引擎的网页链接和文本摘要,给AI/RAG应用增加联网搜索功
    为什么需要WebSearchAPI?各类AINative应用、RAG应用、AIAgent智能体在开发过程都会遇到联网获取互联网网页信息的需求,此时需要得到原始网页链接以及文本摘要,以用于给pipeline中的大模型作为上下文总结使用。但目前仅国外的搜索引擎例如Bing、Google提供此类WebSearch......
  • Mybatis-Flex的增、删、改、查以及swagger (knife4J)的使用
    现代Java开发中,Mybatis-Flex是一个功能强大的Java持久层框架,使数据库操作高效灵活,而Swagger(Knife4J)则改善了API文档化与测试体验,两者结合能提高效率、增强协作、保证代码质量。本文将详细描述Mybatis-Flex增、删、改、查操作及与Swagger(Knife4J)协同使用,以下均已C......
  • swagger2.9.2 和 springboot3.3.4版本冲突问腿
    swagger2.9.2和springboot3.3.4版本冲突问腿问题描述:当我们使用swagger2.9.2版本的时候,如果恰好我们使用的springboot版本是3.x版本,会出现启动报错的问题解决办法:直接使用swagger3.x版本和springboot3.x版本解决步骤:1.导入swagger3.x版本的maven依赖......
  • Sringboot整合swagger
    1、引入依赖​放入pom.xml文件中的下<!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><!--swaggerui-->......
  • 【Swagger】Swagger入门和一些常见的问题
    什么是Swaggerswagger(丝袜哥)是当下比较流行的实时接口文档生成工具。前后端分离后,前后端交流比较重要的东西,就是接口文档。离线文档,最大的弊端就是接口程序发生变动的时候,需要回过头来维护上面的内容,确实比较玛法。实时接口文档可以根据代码来自动生成相应的接口文档。根据代......
  • 【Java】【Swagger】——接口过滤
    在前后端分离时代,Swagger能够实时更新API,十分好用。那么如果根据实际业务需要,展示接口呢?前提已经成功使用Swagger。知道增加 @Bean注解增加分组。此时不同的分组就涉及到不同的过滤。如何过滤接口?增加注解@ApiIgnoreapis():指定包名paths:过滤url增加注解@ApiIgnor......
  • 曾经风靡一时的微信公众号会被小程序平替吗?
    微信小程序和微信公众号是微信平台上两种不同类型的应用,虽然它们都可以在微信生态系统中提供信息传播和互动功能,但它们在用户体验、功能定位以及开发维护等方面存在明显差异。以下是具体分析:曾经风靡一时的微信公众号会被小程序平替吗?用户体验小程序:无需下载安装,可以直接在......
  • MySQL 扛不住了,来试试这款平替的“国产化改造”必入手的国产数据库吧!
    作者:Billmay表妹随着数字化转型的加速,企业对数据库的拓展性及高可用提出更高的要求。传统的集中式关系型数据库MySQL凭借多样化的功能以及开源多样化生态,获得国内数据库市场业务开发、运维等用户的高度认可。但随着经济迅猛发展,业务数据呈现“井喷”式增速,同时伴随着数据安全及......