模型绑定不适用于 asp.net 核心 Web api 控制器操作方法中的 Stream 类型参数。(即使使用自定义流输入格式化程序)

Posted

技术标签:

【中文标题】模型绑定不适用于 asp.net 核心 Web api 控制器操作方法中的 Stream 类型参数。(即使使用自定义流输入格式化程序)【英文标题】:Model binding not working with Stream type parameter in asp.net core webapi controller action method.(Even with custom streaminputformatter) 【发布时间】:2016-12-27 13:29:45 【问题描述】:

我想将文件上传到 asp net core web api 控制器操作方法。我将内容类型作为“应用程序/八位字节流”发送。我创建了名为 StreamInputFormatter 的自定义输入格式化程序。 streaminputformatter 被调用,但控制器中的操作方法没有被调用?并且我收到错误

“InvalidOperationException:此流不支持超时。”

StreamInputFormatter:

public class StreamInputFormatter : IInputFormatter
    
        public bool CanRead(InputFormatterContext context)
        
            if (context == null)
            
                throw new ArgumentNullException(nameof(context));
            
            var contentType = context.HttpContext.Request.ContentType;
            if (contentType == "application/octet-stream")
            
                return true;
            
            return false;
        

        public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
        
            if (context == null)
            
                throw new ArgumentNullException(nameof(context));
            


            var memoryStream = new MemoryStream();
            context.HttpContext.Request.Body.CopyTo(memoryStream);

            return InputFormatterResult.SuccessAsync(memoryStream);
        
    

控制器动作方法:

        [HttpPost("documentType")]
        public async Task<IActionResult> CreateJob(string documentType, [FromBody]Stream template)
        

        

【问题讨论】:

您在哪里使用了 StreamInputFormatter?对于 Startup.cs 中的中间件?可以分享代码吗? 你试过字节数组吗? [FromBody]byte[] 模板 【参考方案1】:

听起来您收到此错误是因为DefaultObjectValidator 迭代了您不受支持的Stream 属性(有关一些信息,请参阅此issue)。

要跳过流模型验证,您可以添加

        services.AddMvc(options =>
        
            ...

            options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream)));
        );

到你的配置。

【讨论】:

当您有一个复杂的类型,验证将无法帮助您使用 SuppressChildValidationMetadataProvider 时,最好的方法是跳过它【参考方案2】:

对于寻找完整示例的任何人,以下内容可能会有所帮助:

Startup.cs

services.AddMvc(options =>

    options.InputFormatters.Add(new StreamInputFormatter());
    options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Stream)));
);

StreamInputFormatter.cs

public class StreamInputFormatter : IInputFormatter

    // enter your list here...
    private readonly List<string> _allowedMimeTypes = new List<string>
         "application/pdf", "image/jpg", "image/jpeg", "image/png", "image/tiff", "image/tif", "application/octet-stream" ;


    public bool CanRead(InputFormatterContext context)
    
        if (context == null)
        
            throw new ArgumentNullException(nameof(context));
        
        var contentType = context.HttpContext.Request.ContentType;
        if (_allowedMimeTypes.Any(x => x.Contains(contentType)))
        
            return true;
        

        return false;
    

    public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
    
        if (context == null)
        
            throw new ArgumentNullException(nameof(context));
        

        // enable stream rewind or you won't be able to read the file in the controller   
        var req = context.HttpContext.Request;
        req.EnableRewind();

        var memoryStream = new MemoryStream();
        context.HttpContext.Request.Body.CopyTo(memoryStream);
        req.Body.Seek(0, SeekOrigin.Begin);
        return InputFormatterResult.SuccessAsync(memoryStream);
    

然后是控制器:

public class FileController : BaseController

    [HttpPost("customer/customerId/file", Name = "UploadFile")]
    [SwaggerResponse(StatusCodes.Status201Created, typeof(UploadFileResponse))]
    [Consumes("application/octet-stream", new string[]  "application/pdf", "image/jpg", "image/jpeg", "image/png", "image/tiff", "image/tif")]
    public async Task<IActionResult> UploadFile([FromBody] Stream file, [FromRoute] string customerId, [FromQuery] FileQueryParameters queryParameters)
    
        // file processing here
    

【讨论】:

return InputFormatterResult.SuccessAsync(context.HttpContext.Request.Body); 在不复制流的情况下对我有用。

以上是关于模型绑定不适用于 asp.net 核心 Web api 控制器操作方法中的 Stream 类型参数。(即使使用自定义流输入格式化程序)的主要内容,如果未能解决你的问题,请参考以下文章

模型绑定不适用于 ASP.NET Core 2 WebAPI 中的 POST 请求

RedirectToAction 不适用于 Tempdata - ASP.net 核心 MVC

[转] ASP.NET MVC 模型绑定的功能和问题

ASP.NET MVC 模型验证不适用于控制器构造函数之一

通过 Internet 公开我的 asp.net 核心 Web 应用程序

如何通过asp.net核心中的属性名称从模型中获取验证属性