.NET6之MiniAPI(二):request
为了方便说明这个系列的文章,我引入了一个业务场景,就是一个简单的考试系统(仅作文章Demo案例),ER图见下图。1、系统有题库,试题有答案,分试题类型和试题类别;2、系统有考生用户,可以从题库组织试卷,分配给考生,考生可以考试记录考试结果。
app.MapGet("/", () => "Hello .NET Mini API!");
Mini API一大好处是简单明了,拿来就用,比如上面的这行代码,MapGet的参数有两个,第一个是路由信息,第二个实现方法,总体意思就是“这个通道做什么”(这个通道是第一个参数,做什么第二个参数),在这里,第二个参数其实是Lambda表达式,也可以换成一个方法(函数),这个方法是是静态方法也好,实例方法也罢,主要是能完成干什么就可以。
接下来再细化一些,看一下这两个参数:
第一个参数:
app.MapGet("/question/{id:int}", (int id) => $"查询ID为{id}试题");
这时请求url为:/question/1,并且这里作了限制,必须为整型,如果是字符或小数,这里就报404了。如url换成/question/-1,也是可以通过的,如下:
但数据库里肯定是没有-1这样的ID(数据是从1开始增量为1的值),所以这块还要加强路由的规则:
app.MapGet("/question/{id:min(1)}", (int id) => $"查询ID为{id}试题");
这样不但卡住负数,也把0排序在外了。如果换成uint会怎么样?
app.MapGet("/question/{id:uint}", (uint id) => $"查询ID为{id}试题");
报的错是从注入容器中找不到uint,这是因为官方的路由参数约束里并没有对uint处理,那如果我们想用无符号整型作参数该怎么办呢?那就自定义路由约束吧。
var builder = WebApplication.CreateBuilder();
builder.Services.AddRouting(options =>
{
options.ConstraintMap["Uint"] = typeof(MyRouteConstraint.Uint);
});
var app = builder.Build();
//参数路由
app.MapGet("/question/{id:Uint}", (uint id) => $"查询ID为{id}试题");
app.Run();
namespace MyRouteConstraint
{
public class Uint : IRouteConstraint
{
public bool Match(HttpContext? httpContext, IRouter? route, string routeKey,RouteValueDictionary values, RouteDirection routeDirection)
{
if (values == null || values.Count == 0 || string.IsNullOrWhiteSpace(routeKey))
{
return false;
}
var result = uint.TryParse(values[routeKey].ToString(), out uint value);
if (result)
{
return true;
}
return false;
}
}
}
关于路由的约束还支持正则,具体见如下代码:
app.MapGet("/area/{postcode:regex(^[0-9]{{3}}-[0-9]{{4}}$)}", (string postcode) => $"邮编:{postcode}");
第二个参数:
1、从query参数中获取数据:
app.MapGet("/answer", ([FromQuery(Name="id")]int answerId) => $"[FromQUery]-请求的AnswerID为{answerId}");
2、从header获取数据
app.MapGet("/answers", ([FromHeader(Name = "key")] string secretkey ) => $"[FromHeader]-secretkey为{secretkey}");
3、从路由中获取数据
app.MapGet("/question/{questiontype}", ([FromRoute]string questionType) => $"[FromRoute]-questiontype={questionType}");
4、从body中获取数据
app.MapPost("/answer", ([FromBody] Answer answer) => $"[FromBody]-answer:{System.Text.Json.JsonSerializer.Serialize(answer)}");
5、从form表单中获取数据(错误,.net6不支持,因为这是api)
6、从Service容器中获取数据,以后说
上面From都是显式的从Request不同区域取数据,这里要提问个问题,你可以把From特性移除,看各个接口访问是否正常,为什么?
自定义参数绑定
我们说第二个参数是个方法,如果这个方法的参数比较复杂,那该怎么处理?官方提供了两个自定义绑定参数的方式。看下面的demo,比如我要查询某个区域的所有酒店,传入的参数是一个坐标组,然后就可以在后台查询出这个范围内的酒店,因为要接收一个复杂类型,但用Request.Query接收到的是字符串,所以这两个自定义绑定就负责完成转换工作(注:当然可以不用这个方式,Post一个Json的Body也是可以的)
using System.Reflection;
var app= WebApplication.Create();
//自定义参数绑定 area=[(35.721875, 139.786564),(35.723903, 139.803464),(35.705806, 139.806078),(35.705118, 139.779927)]
app.MapGet("/area1/hotel", (Area1 area) => $"TryParse Area1:{System.Text.Json.JsonSerializer.Serialize(area)}");
app.MapGet("/area2/hotel", (Area2 area) => $"BindAsync Area2:{System.Text.Json.JsonSerializer.Serialize(area)}");
app.Run();
public class Area1
{
public Coordinates[]? Coordinateses { get; set; }
public static bool TryParse(string? value, IFormatProvider? provider, out Area1? area)
{
var CoordinatesGroupStrings = value?.Split(new string[] { "[(", ")]", "),(" },
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (CoordinatesGroupStrings != null)
{
var coordinatesList = new List<Coordinates>();
foreach (var coordinateGroupString in CoordinatesGroupStrings)
{
var coordinateStrings = coordinateGroupString.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var latitudeResult = double.TryParse(coordinateStrings[0], out double latitude);
var longitudeResult = double.TryParse(coordinateStrings[1], out double longitude);
if (latitudeResult && longitudeResult)
{
coordinatesList.Add(new Coordinates(latitude, longitude));
}
}
area = new Area1 { Coordinateses = coordinatesList.ToArray() };
return true;
}
area = null;
return false;
}
}
public record Coordinates(double Latitude, double Longitude);
public class Area2
{
public Coordinates[]? Coordinateses { get; set; }
public static ValueTask<Area2?> BindAsync(HttpContext context, ParameterInfo parameter)
{
var CoordinatesGroupStrings = context.Request.Query["area"].ToString().Split(new string[] { "[(", ")]", "),(" },
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (CoordinatesGroupStrings != null)
{
var coordinatesList = new List<Coordinates>();
foreach (var coordinateGroupString in CoordinatesGroupStrings)
{
var coordinateStrings = coordinateGroupString.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var latitudeResult = double.TryParse(coordinateStrings[0], out double latitude);
var longitudeResult = double.TryParse(coordinateStrings[1], out double longitude);
if (latitudeResult && longitudeResult)
{
coordinatesList.Add(new Coordinates(latitude, longitude));
}
}
return ValueTask.FromResult<Area2?>(new Area2 { Coordinateses = coordinatesList.ToArray() });
}
return ValueTask.FromResult<Area2?>(null);
}
}
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524