使用 Kestrel 和中间件如何确定从 Post 或 Put 请求传入的类类型?

问题描述 投票:0回答:2

我正在使用 .net 8 和 Kestrel。我正在尝试获取任何 Post 或 Put 请求的类类型。

我的端点代码看起来像这样:

endpoints.MapPost($"{_basePath}/registerMyObject", [AllowAnonymous] async ([FromBody] MyOject myObject) =>
 {
   // my code
 }

我的中间件看起来像:

public class ValidateModelMiddleware : IMiddleware
{
    private ITokenService _tokenService;
    private IServiceProvider _serviceProvider;


    public ValidateModelMiddleware([FromServices] ITokenService tokenService, [FromServices] IServiceProvider serviceProvider)
    {
        _tokenService = tokenService;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {

        if (context.Request.Method == "POST" || context.Request.Method == "PUT")
        {
               // I need to figure out the type that was passed in.  For instance 
        }
     }

因此,当收到 registerMyObject 请求时,我想获取 Type MyOject,以便我可以将该 json 文本直接反序列化到该类中。如果使用不同的 Post / Put 请求并且它具有不同的类类型,那么我想获取相应的类型。有办法做到这一点吗?

c# asp.net-core middleware .net-8.0 kestrel-http-server
2个回答
0
投票

您可以根据 jsonstring“keys”是否与哪个类类型字段匹配来选择要反序列化的类型。例如:
如果有

ClassA
ClassB
都实现
MyObject

    public class ClassA  :MyObject
    {
        public string Name { get; set; }
        public string Address { get; set; } 
    }
    public class ClassB : MyObject
    {
        public string Name { get; set; }
        public string Age { get; set; }
    }

您可以使用以下代码来获取与 jsonstring 匹配的“classResult”字符串。

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var classResult = "";
            //Get JsonString
            var bodyStr = "";
            using (StreamReader reader
                      = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true))
            {
                bodyStr = await reader.ReadToEndAsync();
            }
            //Parse to JObject and get the keys
            var jobject = (JObject)JsonConvert.DeserializeObject(bodyStr);
            var keys = jobject.Properties().Select(p => p.Name);

            //Get all types implement "MyObject"
            var parenttype = typeof(MyObject);
            var types = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(s => s.GetTypes())
                .Where(p => parenttype.IsAssignableFrom(p));

            //Compare which type matches keys
            foreach (var type in types)
            {
                if (Match(keys, type))
                {
                    classResult=type.Name;
                }
            }

            await next(context);
        }

        //Method to compare if keys matches all the fields of a Class type
        private bool Match(IEnumerable<string>? keys,Type type)  
        {
            var fieldNames =type .GetProperties().Select(field => field.Name);
            if (!keys.Except(fieldNames).Any() && !fieldNames.Except(keys).Any())
            {
                return true;
            }
            else
            {
                return false;
            }
        }

0
投票

如果您使用的是最小 API,那么您可以利用 端点过滤器(例如此处):

app.MapPost("/reports", (ReportDTO activityReport) => ...)
    .AddEndpointFilter(async (context, next) =>
    {
        // collection will contain ReportDTO activityReport itself:
        IList<object?> mappedArguments = context.Arguments;
        return await next(context);
    });

如果您希望这是一个中间件,那么对于端点路由,您可以使用

IEndpointFeature
(中间件应在适当的位置注册,另请注意,对于控制器的处理可能会有所不同):

// lambda is analog for you InvokeAsync method
app.Use(async (context, next) =>
{
    var endpointFeature = context.Features.Get<IEndpointFeature>();
    var endpointMetadataCollection = endpointFeature.Endpoint.Metadata;
    var acceptsMetadata = endpointMetadataCollection.GetMetadata<AcceptsMetadata>();
    var requestType = acceptsMetadata.RequestType; // typeof(ReportDTO)
    // ...
    await next();
});

显然第一种方法在性能方面应该更好,因为您不需要多次绑定数据

© www.soinside.com 2019 - 2024. All rights reserved.