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

书接上回,上一章介绍了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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923536.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt界面篇:QMessageBox高级用法

1、演示效果 2、用法注意 2.1 设置图标 用于显示实际图标的pixmap取决于当前的GUI样式。也可以通过设置icon pixmap属性为图标设置自定义pixmap。 QMessageBox::Icon icon(

【第二讲】Spring Boot 3.4.0 新特性详解:新的依赖管理功能

Spring Boot 3.4.0 版本引入了一些显著的改进&#xff0c;其中之一就是新的依赖管理功能。这些改进不仅提升了依赖管理的便利性和一致性&#xff0c;还增强了项目的可维护性和可扩展性。本文将详细介绍 Spring Boot 3.4.0 中新的依赖管理功能&#xff0c;提供具体的使用示例和场…

阿里发布 EchoMimicV2 :从数字脸扩展到数字人 可以通过图片+音频生成半身动画视频

EchoMimicV2 是由阿里蚂蚁集团推出的开源数字人项目&#xff0c;旨在生成高质量的数字人半身动画视频。以下是该项目的简介&#xff1a; 主要功能&#xff1a; 音频驱动的动画生成&#xff1a;EchoMimicV2 能够使用音频剪辑驱动人物的面部表情和身体动作&#xff0c;实现音频与…

STM32C011开发(3)----Flash操作

STM32C011开发----3.Flash操作 概述硬件准备视频教学样品申请源码下载参考程序生成STM32CUBEMX串口配置堆栈设置串口重定向FLASH数据初始化FLASH 读写演示 概述 STM32C011 系列微控制器内置 Flash 存储器&#xff0c;支持程序存储与数据保存&#xff0c;具备页面擦除、双字写入…

银河麒麟桌面系统——桌面鼠标变成x,窗口无关闭按钮的解决办法

银河麒麟桌面系统——桌面鼠标变成x&#xff0c;窗口无关闭按钮的解决办法 1、支持环境2、详细操作说明步骤1&#xff1a;用root账户登录电脑步骤2&#xff1a;导航到kylin-wm-chooser目录步骤3&#xff1a;编辑default.conf文件步骤4&#xff1a;重启电脑 3、结语 &#x1f49…

【自动化Selenium】Python 网页自动化测试脚本(上)

目录 1、Selenium介绍 2、Selenium环境安装 3、创建浏览器、设置、打开 4、打开网页、关闭网页、浏览器 5、浏览器最大化、最小化 6、浏览器的打开位置、尺寸 7、浏览器截图、网页刷新 8、元素定位 9、元素交互操作 10、元素定位 &#xff08;1&#xff09;ID定位 &…

【PTA】【数据库】【SQL命令】编程题2

数据库SQL命令测试题2 测试题目录 10-1 查询“李琳”老师所授课程的课程名称10-2 查询成绩比所有课程的平均成绩高的学生的学号及成绩10-3 创建带表达式的视图StuView10-4 从视图PerView中查询数据10-5 查询工资高于在“HR”部门工作的所有员工的工资的员工信息10-6 查询选修的…

深入浅出摸透AIGC文生图产品SD(Stable Diffusion)

hihi,朋友们,时隔半年(24年11月),终于能腾出时间唠一唠SD了🤣,真怕再不唠一唠,就轮不到SD了,技术更新换代是在是太快! 朋友们,最近(24年2月)是真的没时间整理笔记,每天都在疯狂的学习Stable Diffusion和WebUI & ComfyUI,工作实在有点忙,实践期间在飞书上…

蓝桥杯c++算法秒杀【6】之动态规划【下】(数字三角形、砝码称重(背包问题)、括号序列、异或三角:::非常典型的必刷例题!!!)

别忘了请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01;! ! ! ! &#xff01; 关注博主&#xff0c;更多蓝桥杯nice题目静待更新:) 动态规划 三、括号序列 【问题描述】 给定一个括号序列&#xff0c;要求尽可能少地添加若干括号使得括号序列变得合…

24.100ASK_T113-PRO 驱动摄像头(V4L2)

1.在buildroot 中使能 V4L库 使用make menuconfig命令之后弹出编译菜单选项&#xff1a; 2.按下 / 输入 "libv4l 后回车进行搜索&#xff0c;有2个搜索结果&#xff0c; 3.按下 1 进行跳转 4.按下 / 输入 fswebcam 后回车进行搜索&#xff0c;有1个搜索结果&#xff0c; …

【测试工具JMeter篇】JMeter性能测试入门级教程(二)出炉,测试君请各位收藏了!!!

上篇文章&#xff1a;CSDN 我们介绍了JMeter的一些原理介绍&#xff0c;以及安装配置和启动流程&#xff0c;本文我们就来讲讲JMeter如何使用。 一、JMeter目录结构组成 1. 根目录 Jmeter安装包解压后的根目录如下图&#xff1a; 1.1 backups目录&#xff1a;脚本备份目录&am…

C语言学习 12(指针学习1)

一.内存和地址 1.内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&#xff0c;把你放在楼⾥&#xff0c;楼上有100个房间&#xff0c;但是房间没有编号&#xff0c;你的⼀个朋友来找你玩&#xff0c;如果想找到你&#xff0c;就得挨…

【pyspark学习从入门到精通19】机器学习库_2

目录 估计器 分类 回归 聚类 管道 估计器 估计器可以被看作是需要估算的统计模型&#xff0c;以便对您的观测值进行预测或分类。 如果从抽象的 Estimator 类派生&#xff0c;新模型必须实现 .fit(...) 方法&#xff0c;该方法根据在 DataFrame 中找到的数据以及一些默认或…

微服务篇-深入了解使用 RestTemplate 远程调用、Nacos 注册中心基本原理与使用、OpenFeign 的基本使用

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 认识微服务 1.1 单体架构 1.2 微服务 1.3 SpringCloud 框架 2.0 服务调用 2.1 RestTemplate 远程调用 3.0 服务注册和发现 3.1 注册中心原理 3.2 Nacos 注册中心 …

C语言解析命令行参数

原文地址&#xff1a;C语言解析命令行参数 – 无敌牛 欢迎参观我的个人博客&#xff1a;无敌牛 – 技术/著作/典籍/分享等 C语言有一个 getopt 函数&#xff0c;可以对命令行进行解析&#xff0c;下面给出一个示例&#xff0c;用的时候可以直接copy过去修改&#xff0c;很方便…

深度学习:GPT-1的MindSpore实践

GPT-1简介 GPT-1&#xff08;Generative Pre-trained Transformer&#xff09;是2018年由Open AI提出的一个结合预训练和微调的用于解决文本理解和文本生成任务的模型。它的基础是Transformer架构&#xff0c;具有如下创新点&#xff1a; NLP领域的迁移学习&#xff1a;通过最…

过滤条件包含 OR 谓词,如何进行查询优化——OceanBase SQL 优化实践

这篇博客涉及两个点&#xff0c;一个是 “OR Expansion 改写”&#xff0c;另一个是 “基于代价的改写”。 背景 在写SQL查询时&#xff0c;难以避免在过滤条件中使用 OR 谓词&#xff0c;但其往往会导致索引利用效率下降的问题 。本文将分享如何通过查询改写的2种方式进行优化…

C语言函数递归经典题型——汉诺塔问题

一.汉诺塔问题介绍 Hanoi&#xff08;汉诺&#xff09;塔问题。古代有一个梵塔&#xff0c;塔内有3个座A、B、C&#xff0c;开始时&#xff21;座上有64个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。有一个老和尚想把这64个盘子从&#xff21;座移…

【Python】九大经典排序算法:从入门到精通的详解(冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、计数排序、基数排序、桶排序)

文章目录 1. 冒泡排序&#xff08;Bubble Sort&#xff09;2. 选择排序&#xff08;Selection Sort&#xff09;3. 插入排序&#xff08;Insertion Sort&#xff09;4. 归并排序&#xff08;Merge Sort&#xff09;5. 快速排序&#xff08;Quick Sort&#xff09;6. 堆排序&…

lua除法bug

故事背景&#xff0c;新来了一个数值&#xff0c;要改公式。神奇的一幕出现了&#xff0c;公式算出一个非常大的数。排查是lua有一个除法bug,1除以大数得到一个非常大的数。 function div(a, b)return tonumber(string.format("%.2f", a/b)) end print(1/73003) pri…