我正在尝试在ASP.net 5中访问请求的原始输入正文/流。过去,我能够将输入流的位置重置为0并将其读入内存流但是当我尝试时要从上下文中执行此操作,输入流为null或抛出错误(System.NotSupportedException =>“不支持指定的方法。”)。
在下面的第一个例子中,如果我将控制器方法的参数对象类型声明为动态,我可以在控制器中访问原始请求。由于各种原因,这不是解决方案,我还需要在身份验证过滤器中访问原始请求正文。
此示例有效,但不是一个合理的解决方案:
[HttpPost("requestme")]
public string GetRequestBody([FromBody] dynamic body)
{
return body.ToString();
}
引发错误:
[HttpPost("requestme")]
public string GetRequestBody()
{
var m = new MemoryStream();
Request.Body.CopyTo(m);
var contentLength = m.Length;
var b = System.Text.Encoding.UTF8.GetString(m.ToArray());
return b;
}
引发错误:
[HttpPost("requestme")]
public string GetRequestBody()
{
Request.Body.Position = 0;
var input = new StreamReader(Request.Body).ReadToEnd();
return input;
}
引发错误:
[HttpPost("requestme")]
public string GetRequestBody()
{
Request.Body.Position = 0;
var input = new MemoryStream();
Request.Body.CopyTo(input);
var inputString = System.Text.Encoding.UTF8.GetString(input.ToArray());
return inputString;
}
我需要访问我正在构建的API的每个请求的原始请求主体。
非常感谢任何帮助或指示!
编辑:
以下是我想阅读请求正文的代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Http;
namespace API.Filters
{
public class CustomAuthorizationAttribute : Attribute, IAuthorizationFilter
{
public CustomAuthorizationAttribute()
{ }
public void OnAuthorization(AuthorizationContext context)
{
if (context == null)
throw new ArgumentNullException("OnAuthorization AuthorizationContext context can not be null.");
else
{
if (this.AuthorizeCore(context.HttpContext) == false)
{
// Do Other Stuff To Check Auth
}
else
{
context.Result = new HttpUnauthorizedResult();
}
}
}
protected virtual bool AuthorizeCore(HttpContext httpContext)
{
var result = false;
using (System.IO.MemoryStream m = new System.IO.MemoryStream())
{
try
{
if (httpContext.Request.Body.CanSeek == true)
httpContext.Request.Body.Position = 0;
httpContext.Request.Body.CopyTo(m);
var bodyString = System.Text.Encoding.UTF8.GetString(m.ToArray());
return CheckBody(bodyString); // Initial Auth Check returns true/false <-- Not Shown In Code Here on Stack Overflow
}
catch (Exception ex)
{
Logger.WriteLine(ex.Message);
}
}
return false;
}
}
}
当调用标记有CustomAuthorization属性的控制器方法时,将访问此代码。
[Filters.CustomAuthorizationAuthorization]
[HttpPost]
public ActionResult Post([FromBody]UserModel Profile)
{
// Process Profile
}
答案 0 :(得分:8)
Request.Body
的实现取决于控制器操作。
如果操作包含由Microsoft.AspNet.WebUtilities.FileBufferingReadStream
实现的参数,则支持搜索(Request.Body.CanSeek == true
)。此类型还支持设置Request.Body.Position
。
但是,如果您的操作不包含任何参数,则由Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody
实施,不支持搜索(Request.Body.CanSeek == false
)。这意味着您无法调整Position
属性,只能开始阅读流。
这种差异可能与MVC需要从请求体中提取参数值这一事实有关,因此需要读取请求。
在您的情况下,您的操作没有任何参数。因此使用Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody
,如果您尝试设置Position
属性,则会引发异常。
<小时/> 解决方案:要么不设置位置,要么检查您是否 首先设置位置:
if (Request.Body.CanSeek)
{
// Reset the position to zero to read from the beginning.
Request.Body.Position = 0;
}
var input = new StreamReader(Request.Body).ReadToEnd();
答案 1 :(得分:6)
您在最后三个片段中看到的异常是尝试多次读取请求体的直接后果 - 一次是MVC 6,一次是自定义代码 - 使用IIS或WebListener等流式传输主机时。您可以查看此SO问题以获取更多信息:Read body twice in Asp.Net 5。
尽管如此,我只是期望在使用application/x-www-form-urlencoded
时发生这种情况,因为对于MVC来说,使用文件上传等冗长请求开始读取请求流是不安全的。如果情况并非如此,则可能是您应该在https://github.com/aspnet/Mvc报告的MVC错误。
对于变通方法,您应该看一下这个SO答案,它解释了如何使用context.Request.ReadFormAsync
或添加手动缓冲:Read body twice in Asp.Net 5
app.Use(next => async context => {
// Keep the original stream in a separate
// variable to restore it later if necessary.
var stream = context.Request.Body;
// Optimization: don't buffer the request if
// there was no stream or if it is rewindable.
if (stream == Stream.Null || stream.CanSeek) {
await next(context);
return;
}
try {
using (var buffer = new MemoryStream()) {
// Copy the request stream to the memory stream.
await stream.CopyToAsync(buffer);
// Rewind the memory stream.
buffer.Position = 0L;
// Replace the request stream by the memory stream.
context.Request.Body = buffer;
// Invoke the rest of the pipeline.
await next(context);
}
}
finally {
// Restore the original stream.
context.Request.Body = stream;
}
});
答案 2 :(得分:2)
我刚才有同样的问题。从方法签名中删除参数,然后按照您想要的方式阅读Request.Body
Stream。
答案 3 :(得分:0)
您需要调用Request.EnableRewind()以允许重绕流,以便您可以阅读它。
string bodyAsString;
Request.EnableRewind();
using (var streamReader = new StreamReader(Request.Body, Encoding.UTF8))
{
bodyAsString = streamReader.ReadToEnd();
}