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

.NET9 - Swagger平替Scalar详解(四)的更多相关文章

  1. .NET DLL 保护措施详解(四)各操作系统运行情况

    我准备了WEB应用程序及WinForm应用程序,分别在WIN SERVER 2012/2008/2003.Win7/10上实测,以下为实测结果截图: 2012 2008 2003 WIN7 WIN10 ...

  2. logback -- 配置详解 -- 四 -- <filter>

    附: logback.xml实例 logback -- 配置详解 -- 一 -- <configuration>及子节点 logback -- 配置详解 -- 二 -- <appen ...

  3. pika详解(四) channel 通道

    pika详解(四) channel 通道   本文链接:https://blog.csdn.net/comprel/article/details/94662394 版权 ​ channel通道 通道 ...

  4. C++11 并发指南六(atomic 类型详解四 C 风格原子操作介绍)

    前面三篇文章<C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)>.<C++11 并发指南六( <atomic> 类型详解二 std::at ...

  5. View绘制详解(四),谝一谝layout过程

    上篇博客我们介绍了View的测量过程,这只是View显示过程的第一步,第二步就是layout了,这个我们一般译作布局,其实就是在View测量完成之后根据View的大小,将其一个一个摆放在ViewGro ...

  6. Android Studio 插件开发详解四:填坑

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78265540 本文出自[赵彦军的博客] 在前面我介绍了插件开发的基本流程 [And ...

  7. webRTC中语音降噪模块ANS细节详解(四)

    上篇(webRTC中语音降噪模块ANS细节详解(三))讲了噪声的初始估计方法以及怎么算先验SNR和后验SNR. 本篇开始讲基于带噪语音和特征的语音和噪声的概率计算方法和噪声估计更新以及基于维纳滤波的降 ...

  8. Android开发之基本控件和详解四种布局方式

    Android中的控件的使用方式和iOS中控件的使用方式基本相同,都是事件驱动.给控件添加事件也有接口回调和委托代理的方式.今天这篇博客就总结一下Android中常用的基本控件以及布局方式.说到布局方 ...

  9. Activity详解四 activity四种加载模式

    先看效果图: 1概述 Activity启动方式有四种,分别是: standard singleTop singleTask singleInstance 可以根据实际的需求为Activity设置对应的 ...

  10. Swagger服务API治理详解

    swager2的简介 在App后端开发中经常需要对移动客户端(Android.iOS)提供RESTful API接口,在后期版本快速迭代的过程中,修改接口实现的时候都必须同步修改接口文档,而文档与代码 ...

随机推荐

  1. Blender - 动画demo体后感

    Blender 一个非常不错的免费的3D.2D软件 超级不错 我初步的按照网上的动画教程,做了一个很简单的 不断跳动的独眼球 为什么独眼?一开始我是画了两个眼睛,结果最后总是出了点问题,没有办法cop ...

  2. 搭建ipv6并发代理池

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 学习目标 ounter(l ...

  3. UWP 通过 .NET 9 和Native AOT 的支持实现 UWP 应用的现代化

    微软(9 月 11 日)发布博文,微软正在预览对 .NET 9 的 UWP(通用 Windows 平台)支持,为现有 UWP 开发人员提供一条使用最新的 .NET 和本机 AOT 实现其应用程序现代化 ...

  4. ASP.NET Core Library – FluentValidation

    前言 之前就有写过学习笔记: Asp.net core 学习笔记 Fluent Validation 但都是用一点记入一点,零零散散不好读, 这一篇来稍微整理一下. 主要参考: Fluent Vali ...

  5. 修改Kubernetes主节点(控制节点)名称

    1.修改物理机主机名 hostnamectl set-hostname <hostname> 2.修改 /etc/kubernetes/manifests 目录下的文件,将文件内容包含旧主 ...

  6. web端ant-design-vue Modal.info组件自定义icon和title使用小节

    web端ant-design-vue Modal.info组件自定义icon和title整理小节,最近在项目中用到了自定义icon和title的功能,经过测试发现,如果自定义icon title会自动 ...

  7. KubeSphere 社区双周报| 2024.08.16-08.29

    KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...

  8. Kubernetes 集群中 Ingress 故障的根因诊断

    作者:scwang18,主要负责技术架构,在容器云方向颇有研究. 前言 KubeSphere 是青云开源的基于 Kubernetes 的云原生分布式操作系统,提供了比较炫酷的 Kubernetes 集 ...

  9. 记一次pip/pip3安装uwsgi报错

    背景 本来是想着在centos 7.9的主机上面部署一个flask应用,采用的方式是linux+nginx+uwsgi来进行发布,其他的都没有问题,但是在使用pip安装uwsgi时报错了,安装不成功, ...

  10. 如何将rust日志输出到android终端

    本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议.转载请注明来自 唯你 背景 在 Rust 中,使用 println!打印日志时,输出实际上是发送到标准输出(stdout),而 ...