我有一个由第三方调用的 post 方法。为了验证是他们本人,他们对正文进行加密并将其添加为标头。所以我可以做同样的事情并检查是否有效。
我希望将其作为某种属性来执行,这样我就可以在整个控制器中自动重用它,所以我有:
public class Verify : ActionFilter, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var signature = context.HttpContext.Request.Headers["x-signature"];
var requestBody = await new StreamReader(context.HttpContext.Request.Body).ReadToEndAsync();
var key = "my-secure-key";
if (EncryptTheBody(requestBody, key) != signature)
{
// not valid
context.Result = new UnauthorizedResult();
}
context.HttpContext.Request.Body.Position = 0; //added this - but doesn't work
}
}
效果很好。 除了我需要稍后在控制器操作中获取主体
[HttpPost]
[Verify]
public Task Post(MyModel model){
// model is all null.
}
有什么想法吗?解决方案?更好的方法?
我可以将上面的所有内容复制/粘贴到控制器中,删除属性,然后只需使用字符串主体两次,但想避免。
您可以通过使用请求缓冲来做到这一点,但您必须在第一次读取请求数据之前启用缓冲,包括 ASP.NET 原生读取请求数据。当您进入控制器时,Request.Body 已被读取。 为此,您必须连接一个自定义中间件处理程序,该处理程序必须在添加 ASP.NET 控制器之前运行(在 program.cs/startup.cs 中):
app.Use(async (context, next) =>
{
context.Request.EnableBuffering();
await next();
}
如果您需要在控制器代码之前读取缓冲区,您可以在同一处理程序中使用类似的内容:
app.Use(async (context, next) =>
{
if (context.Request.Method == "POST" || context.Request.Method == "PUT")
{
string bodyString = null;
// this is early enough in the pipeline
context.Request.EnableBuffering();
using var reader = new StreamReader(context.Request.Body,
Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
leaveOpen: true /* important! */);
try
{
bodyString = await reader.ReadToEndAsync();
// reset the stream to start position
if (context.Request.Body.CanSeek)
context.Request.Body.Position = 0;
}
catch(Exception)
{
bodyString = null;
}
// ... do something with the capture data
}
await next();
}
如果您需要在ASP.NET处理完请求数据之后进行捕获,您可以手动重置流位置,然后进行读取:
[HttpPost]
[Verify]
public async Task<object> Post(MyModel model){
{
Context.HttpContext.Request.Body.Position = 0;
// ... READ THE STREAM here as above
}
请参阅此博文。