.NET7 Preview4 之OpenAPI swagger改进
在MiniAPI系列中,《.NET6之MiniAPI(十八):OpenAPI swagger》介绍了swagger在MiniAPI框架中的使用,当时留下很多不足,随着.NET7 Preview4的推出,这方面得到了很大的改进,我还是使用“十八”这篇文章的案例。
如果想参看原来文章,见下面引用:
此次对OpenAPI的提升主要是通过命名空间Microsoft.AspNetCore.OpenApi带来的。
新建API项目,选用minimal api模板,并带有OpenAPI,同时在Nuget升级Swashbuckle.AspNetCore为6.3.1以后的版本,核心代码如下:
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.OpenApi; using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "MiniAPI7_new04-V1", Version = "v1" } ); //添加授权 var schemeName = "Bearer"; c.AddSecurityDefinition(schemeName, new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "请输入不带有Bearer的Token", Name = "Authorization", Type = SecuritySchemeType.Http, Scheme = schemeName.ToLowerInvariant(), BearerFormat = "JWT" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = schemeName } }, new string[0] } }); }); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.EnablePersistAuthorization(); }); } //增 app.MapPost("/test", Results<Ok<Data>, NotFound> (Data data) => { if (data != null) { data.ID = 101; return TypedResults.Ok(data); } else { return TypedResults.NotFound(); } }) .WithTags("all test") .WithOpenApi(operation => { operation.Description = "这是一个神密的功能,用来实现添加"; operation.Summary = "添加Data"; operation.Parameters.Clear(); operation.RequestBody = new OpenApiRequestBody { Description = "添加的数据实体", Required = true, Content = new Dictionary<string, OpenApiMediaType> { { "application/json", new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = new Dictionary<string, OpenApiSchema> { {"ID",new OpenApiSchema{ Type="integer" } }, {"Name",new OpenApiSchema{ Type="string" } }, {"Token",new OpenApiSchema{ Type="string" } } }, }, } } }, }; return operation; }); //删 app.MapDelete("/test/{id}", Results<Ok, NotFound> (int? id) => { if (id.HasValue) { return TypedResults.Ok(); } else { return TypedResults.NotFound(); } }) .WithTags("all test") .WithOpenApi(operation => { operation.Description = "这是一个神密的功能,用来实现删除"; operation.Summary = "按编号删除"; operation.Parameters[0].Description = "编号"; operation.Parameters[0].AllowEmptyValue = true; return operation; }); //改 app.MapPut("/test", (Data data) => { }) .WithTags("all test") .WithOpenApi(operation => { operation.Description = "这是一个神密的功能,用来实现修改"; operation.Summary = "修改Data"; operation.Parameters.Clear(); return operation; }); //查 app.MapGet("/test/{id}", Results<Ok<Data>, NotFound> (HttpRequest request, int? id) => { if (id.HasValue) { return TypedResults.Ok(new Data() { ID = id.Value, Name = "测试", Token = request.Headers["Authorization"] }); } else { return TypedResults.NotFound(); } }) .WithTags("all test") .WithOpenApi(operation => { operation.Description = "这是一个神密的功能,用来实现查询"; operation.Summary = "按编号查询"; operation.Parameters[0].Description = "编号"; operation.Parameters[0].AllowEmptyValue = true; return operation; }); app.Run(); /// <summary> /// 提交数据 /// </summary> class Data { /// <summary> /// 编号 /// </summary> public int ID { get; set; } /// <summary> /// 名称 /// </summary> public string? Name { get; set; } /// <summary> /// Token /// </summary> public string? Token { get; set; } }
当年在做go时,很羡慕它的时间有微秒,纳秒,在做性能优化时,能很小颗粒度的查看引入方法执行的时间,当时.net的DateTime只有毫秒(虽然也有别的办法获取)。现在,在最新的.NET7 Preview4中,DateTime也有微秒和纳秒了,倍感亲切。
纳秒在百位上,没有十位和个位,但这也说明.NET在进化,向高性能进化,在乎微秒和百位纳秒了(哈哈)。
下面是引入这两个时间单位的实现:
namespace System { public struct DateTime { public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond); public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.DateTimeKind kind); public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar); public int Microsecond { get; } public int Nanosecond { get; } public DateTime AddMicroseconds(double value); } public struct DateTimeOffset { public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset); public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset, System.Globalization.Calendar calendar); public int Microsecond { get; } public int Nanosecond { get; } public DateTimeOffset AddMicroseconds(double microseconds); } public struct TimeSpan { public const long TicksPerMicrosecond = 10L; public const long NanosecondsPerTick = 100L; public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds); public int Microseconds { get; } public int Nanoseconds { get; } public double TotalMicroseconds { get; } public double TotalNanoseconds { get; } public static TimeSpan FromMicroseconds(double microseconds); } public struct TimeOnly { public TimeOnly(int day, int hour, int minute, int second, int millisecond, int microsecond); public int Microsecond { get; } public int Nanosecond { get; } } }
.NET Preview5中,给MiniAPI带来了一个参数绑定的功能,看到这个功能,我一下子就开心了,因为它提供了一个把松散的传入数据或注入功能耦合在一起的能力,并且可以根据自己的需求自由组合,结合上原来的Fromxxx(Name=“”)]使用,尤其和谐。
看一下下面的例子,如果每个请求都带有X-UUID,可以直接放在父类里,这样的组合是不是更加灵活多变。
using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/test", ([AsParameters] Order order) => { order.Logger?.LogInformation(order.UUID); }); app.Run(); class Header { [FromHeader(Name = "X-UUID")] public string? UUID { get; set; } } class Order : Header { [FromQuery(Name = "no")] public int OrderNo { get; set; } public ILogger<Order>? Logger { get; set; } }
绑定的参数,不只是class,还可以是其他自定义类型。
结构
struct Order { [FromHeader(Name = "X-UUID")] public string? UUID { get; set; } [FromQuery(Name = "no")] public int OrderNo { get; set; } public ILogger<Order>? Logger { get; set; } }
记录
record Order { [FromHeader(Name = "X-UUID")] public string? UUID { get; set; } [FromQuery(Name = "no")] public int OrderNo { get; set; } public ILogger<Order>? Logger { get; set; } }
结构记录
record struct Order { [FromHeader(Name = "X-UUID")] public string? UUID { get; set; } [FromQuery(Name = "no")] public int OrderNo { get; set; } public ILogger<Order>? Logger { get; set; } }
虽然这只是.NET的一小步,但给开发人员带了一大步,使整个API开发体验得到了,特别对数据接收体验提升了一大截。