.Net Core Middleware - 从请求中获取表单数据

Posted

技术标签:

【中文标题】.Net Core Middleware - 从请求中获取表单数据【英文标题】:.Net Core Middleware - Getting Form Data from Request 【发布时间】:2019-09-27 00:52:56 【问题描述】:

在 .NET Core Web 应用程序中,我使用中间件 (app.UseMyMiddleware) 为每个请求添加一些日志记录:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            else
            
                app.UseExceptionHandler(MyMiddleware.GenericExceptionHandler);
                app.UseHsts();
            

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseMyMiddleware();

            app.UseMvc(routes =>
            
                routes.MapRoute(
                    name: "default",
                    template: "controller=Home/action=Index/id?");
            );
        
        public static void UseMyMiddleware(this IApplicationBuilder app)
        
            app.Use(async (context, next) =>
            
                await Task.Run(() => HitDetails.StoreHitDetails(context));
                await next.Invoke();
            );
        
        public static void StoreHitDetails(HttpContext context)
        
            var config = (IConfiguration)context.RequestServices.GetService(typeof(IConfiguration));
            var settings = new Settings(config);
            var connectionString = config.GetConnectionString("Common");
            var features = context.Features.Get<IHttpRequestFeature>();
            var url = $"features.Scheme://context.Request.Host.Valuefeatures.RawTarget";

            var parameters = new
            
                SYSTEM_CODE = settings.SystemName,
                REMOTE_HOST = context.Connection.RemoteIpAddress.ToString(),
                HTTP_REFERER = context.Request.Headers["Referer"].ToString(),
                HTTP_URL = url,
                LOCAL_ADDR = context.Connection.LocalIpAddress.ToString(),
                AUTH_USER = context.User.Identity.Name
            ;

            using (IDbConnection db = new SqlConnection(connectionString))
            
                db.Query("StoreHitDetails", parameters, commandType: CommandType.StoredProcedure);
            
        

这一切都很好,我可以从请求中获取我需要的大部分内容,但接下来我需要的是 POST 方法上的表单数据。

context.Request.Form 是一个可用选项,但在调试时我将鼠标悬停在它上面并看到“函数评估需要所有线程运行”。如果我尝试使用它,应用程序就会挂起。

我需要做什么才能访问 Request.Form 或者是否有其他属性包含我没有看到的 POST 数据?

【问题讨论】:

这可能是因为您正在使用Task.Run,它将在线程池上运行您的StoreHitDetails,这就是为什么当您将鼠标悬停在Request.Form 上时会看到该消息。为什么不将StoreHitDetails 中的所有逻辑移至UseMyMiddleware?或者在await Task.Run(() =&gt; HitDetails.StoreHitDetails(context)); 完成后尝试使用Request.Form。我希望这会有所帮助。 啊哈当然!不幸的是,我无法移动 StoreHitDetails 逻辑,因为它在其他地方被重用。这个中间件不必在线程池中,它只是我可以让所有东西(bar Form)工作的唯一方法。知道如何在没有 async/await 调用的情况下编写它吗?谢谢, 【参考方案1】:

您可以创建一个单独的中间件而不是内联中间件,然后从那里调用HitDetails.StoreHitDetails

public class MyMiddleware

    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next)
    
        _next = next;
    

    public async Task Invoke(HttpContext context)
    
        HitDetails.StoreHitDetails(context);

        await _next(context);
    


// Extension method used to add the middleware to the HTTP request pipeline.
public static class MiddlewareExtensions

    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    
        return builder.UseMiddleware<MyMiddleware>();
    

这样您就可以继续使用app.UseMyMiddleware();,而不必像您提到的那样使用Task.Run 运行它。

或者你可以尝试调用HitDetails.StoreHitDetails(context)而不用Task.Run包装它

已编辑

检查您的Request 的内容类型是否正确:

if (context.Request.HasFormContentType)

    IFormCollection form;
    form = context.Request.Form; // sync
    // Or
    form = await context.Request.ReadFormAsync(); // async

    string param1 = form["param1"];
    string param2 = form["param2"];
 

【讨论】:

虽然这编译正确并且没有专门在 HitDetails 上调用 await,但由于某种原因,Form 属性仍然是线程锁定的。我什至尝试从 Task 方法中删除 async 和 await ,但得到了相同的结果。 是不是只在调试过程中出现?您是否尝试过在不调试应用程序的情况下运行它? 它似乎没有挂起,但记录的只是 __RequestVerificationToken 和 UserID 属性。我是否在错误的位置查找 POST 数据? 这可能与错误的内容类型有关。我已经编辑了答案并添加了一个检查以查看请求是否具有 FormContentType,即 application/x-www-form-urlencoded。此外,您可以利用await httpContext.Request.ReadFormAsync(); 方法异步读取表单值。 原因是,根据设计,如果尚未读取请求正文,Form 属性将尝试读取它。如果请求没有正确的 Content-Type,这将失败

以上是关于.Net Core Middleware - 从请求中获取表单数据的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 开发-中间件(Middleware)

.Net Core Middleware - 从请求中获取表单数据

4.4管道Middleware简介「深入浅出ASP.NET Core系列」

利用ASP.NET Core的MiddleWare思想处理复杂业务流程

这批.Net程序员水平不行啊!居然ASP.NET Core Middleware都不会用

ASP.NET Core使用Middleware有条件地允许访问路由