C# 缺少内容类型边界

Posted

技术标签:

【中文标题】C# 缺少内容类型边界【英文标题】:C# Missing content-type boundary 【发布时间】:2018-03-18 07:11:03 【问题描述】:

我正在使用 RestEase 客户端库从一项服务向另一项服务发出请求。 它的界面是这样的

public interface IImportService1ApiClient

    [Put]
    [Header("Content-Type", "multipart/form-data")]
    Task<CreateValidationJobResponse> ImportZip([Body] byte[] zipByteArray);

以及端点(.Net Core 1.1、Web Api):

    [HttpPut()]
    [Consumes("multipart/form-data")]
    public async Task<IActionResult> ImportZip()
    
         var zipFile = HttpContext.Request.Form.Files.FirstOrDefault();
     ...

所以我可以提出请求,但是当我尝试从 froms 文件集合中获取文件时出现异常

System.IO.InvalidDataException: Missing content-type boundary.

堆栈跟踪:

 Connection id "0HL8CNQ0E4M94": An unhandled exception was thrown by the application.
System.IO.InvalidDataException: Missing content-type boundary.
   at Microsoft.AspNetCore.Http.Features.FormFeature.GetBoundary(MediaTypeHeaderValue contentType, Int32 lengthLimit)
   at Microsoft.AspNetCore.Http.Features.FormFeature.<InnerReadFormAsync>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()
   at RODIX.Tacs.Services.Import.Api.Controllers.TacsLegacyImportController.<ImportZip>d__3.MoveNext() in C:\Users\borov\Source\Repos\Rodix.Tacs\Rodix.Tacs\services\RODIX.ImportService\src\RODIX.Tacs.Services.Import.Api\Controllers\TacsLegacyImportController.cs:line 32
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationMiddleware.<Invoke>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()

但是当用 Postman 做同样的事情时,一切都很好。 那么边界实际上是什么,为什么我会错过它以及如果真的需要它如何添加它?

【问题讨论】:

也许this comment 会有所帮助。 @NightOwl888 未设置标头内容类型时 - 请求不会超过 [Consumes("multipart/form-data")] 属性,我会收到缺少内容类型异常 @NightOwl888 你是对的。我们不需要修改该标题 【参考方案1】:

如果您只是上传文件,为什么不直接获取 Request.Body 流:

[HttpPost()]
[Consumes("application/zip")]
[Consumes("application/octet-stream")]
public async Task<IActionResult> ImportZip()

    using (var stream = new MemoryStream())
    
        await Request.Body.CopyToAsync(stream);
    

    // do whatever with the file...

我觉得你也可以这样定义:

[HttpPost()]
[Consumes("application/zip")]
[Consumes("application/octet-stream")]
public async Task<IActionResult> ImportZip([FromBody] Stream bodyStream)

    using (var stream = new MemoryStream())
    
        await bodyStream.CopyToAsync(stream);
    

    // do whatever with the file...

【讨论】:

以上是关于C# 缺少内容类型边界的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2 - 缺少内容类型边界

如何以编程方式设置内容类型?获取 org.jvnet.mimepull.MIMEParsingException:缺少开始边界

HttpClient 设置边界与内容类型

如何检查网站是不是缺少阻止内容类型嗅探的标题

提供的“HttpContent”实例无效。它没有带有“边界”参数的“多部分”内容类型标头

C# ASP.NET ASP.NET#命名空间"System.Data"中不存在类型或命名空间名称"Linq"(是否缺少程序集引用?)