.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(() => 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思想处理复杂业务流程