【问题标题】:POST body to parameter through input formatter通过输入格式化程序将正文发布到参数
【发布时间】:2019-08-05 14:59:30
【问题描述】:

我正在尝试编写自己的输入格式化程序,它将读取请求正文,按行拆分并将其传递到控制器操作中的字符串数组参数中。


这可行(将整个主体作为字符串传递):

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore(options =>
    {
        options.InputFormatters.Add(new MyInputFormatter());
    }
}


MyInputFormatter.cs

public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
    using (StreamReader reader = new StreamReader(context.HttpContext.Request.Body))
    {
        return InputFormatterResult.Success(await reader.ReadToEndAsync());
    }
}

MyController.cs

[HttpPost("/foo", Name = "Foo")]
public IActionResult Bar([FromBody] string foo)
{
    return Ok(foo);
}

这不起作用(参数 foo 为空):

MyInputFormatter.cs

public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
    List<string> input = new List<string>();

    using (StreamReader reader = new StreamReader(context.HttpContext.Request.Body))
    {
        while (!reader.EndOfStream)
        {
            string line = (await reader.ReadLineAsync()).Trim();
            input.Add(line);
        }
    }

    return InputFormatterResult.Success(input.ToArray());
}

MyController.cs

[HttpPost("/foo", Name = "Foo")]
public IActionResult Bar([FromBody] string[] foo)
{
    return Ok(string.Join(" ", foo));
}

不同之处在于,在控制器中,我现在接受的是字符串数组而不是字符串,而在格式化程序中,我正在逐行读取输入,最后将其作为数组返回。


我错过了什么? :/


编辑:我的格式化程序实际上看起来如何,或多或少(如果有什么不同的话):

    public class MyInputFormatter : InputFormatter
    {
        public MyInputFormatter()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue(MimeType.URI_LIST)); // "text/uri-list"
        }

        public override bool CanRead(InputFormatterContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context)); // breakpoint here not reached

            if (context.HttpContext.Request.ContentType == MimeType.URI_LIST)
                return true;

            return false;
        }

        protected override bool CanReadType(Type dataType)
        {
            return typeof(string[]).IsAssignableFrom(dataType); // breakpoint here not reached
        }

        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {
            List<string> input = new List<string>(); // breakpoint here not reached

            using (StreamReader reader = new StreamReader(context.HttpContext.Request.Body))
            {
                while (!reader.EndOfStream)
                {
                    string line = (await reader.ReadLineAsync()).Trim();

                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }

                    if (!line.StartsWith("foo-"))
                    {
                        return InputFormatterResult.Failure();
                    }

                    input.Add(line.Substring("foo-".Length));
                }
            }

            return InputFormatterResult.Success(input.ToArray());
        }

【问题讨论】:

    标签: c# .net-core inputformatter


    【解决方案1】:

    我在请求处理程序方法中使用您的代码创建了一个测试输入格式化程序,它工作正常,如下所示:

    public class TestInputFormatter : IInputFormatter
    {
        public bool CanRead(InputFormatterContext context) => true;
    
        public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
        {
            List<string> input = new List<string>();
    
            using (StreamReader reader = new StreamReader(context.HttpContext.Request.Body))
            {
                while (!reader.EndOfStream)
                {
                    string line = (await reader.ReadLineAsync()).Trim();
                    input.Add(line);
                }
            }
    
            return InputFormatterResult.Success(input.ToArray());
        }
    }
    

    我在您的代码中只看到一点可能是错误的 - 您的输入格式化程序的注册。文档说:Formatters are evaluated in the order you insert them. The first one takes precedence. 尝试这样注册:

    options.InputFormatters.Insert(0, new TestInputFormatter());
    

    它在我的测试项目中工作,正是这样的注册。因为当您调用 options.InputFormatters.Add 时,它将被添加到输入格式化程序集合的末尾,并且您的请求可能会由位于该集合中的第一个其他输入格式化程序处理。

    【讨论】:

    • 不是格式化顺序。 :/我一直在调试它,就像根本没有使用格式化程序一样。没有使用格式化程序。它甚至不运行任何 CanRead 或 CanReadType 方法,它只是直接进入具有空值的控制器操作。我已经使用调试器和断点对其进行了测试。还有什么可能导致这种行为?是什么决定了格式化程序是否会被试用?我在格式化程序的 SupportedMediaTypes 中添加了一个内容类型,并且在请求的 Content-Type 标头中使用了完全相同的值,但它仍然没有触发。
    • 我在底部添加了格式化程序的真实外观。我不认为这有什么不同,但以防万一。
    • 问题不在于您的InputFilter 实现,也不在于它在过滤器集合中的注册(因为它有自己的内容类型)。我已经测试了你的MyInputFormatter,它工作正常。你错过了其他东西,如果没有完整的项目来源,很难说什么。
    • 是的,我明白你的意思了。我创建了一个新的 mvc 项目,并在里面添加了控制器操作和格式化程序,它可以工作。 :/ 我会试着四处看看有什么问题...
    【解决方案2】:

    我想通了到底是什么问题。我有一个自定义的 ModelBinder 进行干扰,捕获任何不是字符串的内容和自定义接口的实现(用于其他发布数据)。这就是为什么它适用于字符串和其他输入有效负载(接口的实现),但不适用于字符串数组。该绑定器本应用于查询参数(以便能够处理自定义类型),但最终也触发了此 POST 有效负载。

    【讨论】:

      猜你喜欢
      • 2014-08-18
      • 2015-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      • 2021-07-22
      • 1970-01-01
      • 2016-11-19
      相关资源
      最近更新 更多