Fork me on GitHub

.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);
    }
}
  
  想要更快更方便的了解相关知识,可以关注微信公众号 
 
posted @ 2022-05-11 20:44  桂素伟  阅读(371)  评论(0编辑  收藏  举报