访问原始请求正文

Bad*_*Dev 11 asp.net-core-mvc asp.net-core asp.net-core-webapi

我正在尝试在ASP.net 5中访问请求的原始输入正文/流.过去,我能够将输入流的位置重置为0并将其读入内存流但是当我尝试这样做时从上下文中,输入流为null或抛出错误(System.NotSupportedException =>"不支持指定的方法.").

在下面的第一个示例中,如果我将控制器方法的参数对象类型声明为动态,则可以在控制器中访问原始请求.由于各种原因,这不是一个解决方案,我需要在身份验证过滤器中访问原始请求正文.

此示例有效,但不是一个合理的解决方案:

[HttpPost("requestme")]
public string GetRequestBody([FromBody] dynamic body)
{   
    return body.ToString();
}
Run Code Online (Sandbox Code Playgroud)

引发错误:

[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;
}
Run Code Online (Sandbox Code Playgroud)

引发错误:

[HttpPost("requestme")]
public string GetRequestBody()
{
    Request.Body.Position = 0;
    var input = new StreamReader(Request.Body).ReadToEnd();

    return input;
}
Run Code Online (Sandbox Code Playgroud)

引发错误:

[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;
}
Run Code Online (Sandbox Code Playgroud)

我需要访问我正在构建的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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当调用标记有CustomAuthorization属性的控制器方法时,将访问此代码.

[Filters.CustomAuthorizationAuthorization]
[HttpPost]
public ActionResult Post([FromBody]UserModel Profile)
{
    // Process Profile
}
Run Code Online (Sandbox Code Playgroud)

Hen*_*ema 8

执行Request.Body取决于控制器操作.

如果操作包含由其实现的参数Microsoft.AspNet.WebUtilities.FileBufferingReadStream,则支持seek(Request.Body.CanSeek == true).此类型还支持设置Request.Body.Position.

但是,如果您的操作不包含由其实现的参数Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody,则不支持seek(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();
Run Code Online (Sandbox Code Playgroud)


Kév*_*let 7

您在最后三个片段中看到的例外情况是,在使用IIS或WebListener等流式传输主机时,尝试多次读取请求主体(一次是MVC 6,一次是自定义代码)的直接后果.您可以查看此SO问题以获取更多信息:在Asp.Net 5中读取主体两次.

也就是说,我只希望在使用时发生这种情况application/x-www-form-urlencoded,因为对于MVC而言,如果文件上传等冗长的请求开始读取请求流是不安全的.如果不是这样,那么你可能应该在https://github.com/aspnet/Mvc上报告一个MVC错误.

对于变通方法,您应该看看这个SO答案,它解释了如何使用context.Request.ReadFormAsync或添加手动缓冲:在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;
    }
});
Run Code Online (Sandbox Code Playgroud)