ContentDispositionHeaderValueIdentityExtensions.IsFileDisposition引发ArgumentNullException:值不能为null参数名称:header

时间:2019-06-11 15:00:29

标签: c# asp.net-core .net-core

我有一个旧的.Net Framework客户端,其中包含许多无法更改的安装。我需要处理用于将文件上传到服务器的http发布请求。

以前,我们可以使用HttpContext.Request.Files。但是,由于我们现在使用的是dotnet核心,因此必须使用HttpContext.Request.Form.Files。哪个抛出了错误

“值不能为空。参数名称:header”的堆栈跟踪如下

  在

  Microsoft.Net.Http.Headers.ContentDispositionHeaderValueIdentityExtensions.IsFileDisposition(ContentDispositionHeaderValue   标头)   Microsoft.AspNetCore.Http.Features.FormFeature.InnerReadFormAsync(CancellationToken   cancelToken)   Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()在   Microsoft.AspNetCore.Http.Internal.DefaultHttpRequest.get_Form()

在github上查看asp dotnet核心的源代码,我可以在第26行看到确切的问题here

return header.DispositionType.Equals("form-data")
                && (!StringSegment.IsNullOrEmpty(header.FileName) || !StringSegment.IsNullOrEmpty(header.FileNameStar));

我认为这是客户端将标头信息添加到文件而不是请求本身的问题,但是当我添加一些中间件(如下)以将标头添加到我的请求时,HttpContext.Request。窗体仍会引发异常。我不知道此异常是在创建时引发的,还是我没有正确设置标头,或者这是否是dotnet核心中的错误。 github上存在一些开放的问题可以改善错误消息,但是没有任何迹象表明它们的代码是问题所在。

我只想“修复”报头,因为我知道对该端点的调用将始终是多部分表单数据,理想情况下使用中间件,因此控制器不必处理它。

中间件:

public class FormDataHeaderRepair
{
    private readonly RequestDelegate _next;

    public FormDataHeaderRepair(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var headers = context.Request.Headers;
        if (context.Request.Path.ToString().Equals("/api/MyController/Queue", StringComparison.OrdinalIgnoreCase))
        {
            if (context.Request.Headers["Content-Disposition"].Count == 0)
            {
               var cv = new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue("form-data");
               cv.FileName = "request.xml";
               var stringVersion = cv.ToString();
               context.Request.Headers[HeaderNames.ContentDisposition] = stringVersion;
            }
        }

        await _next(context);
    }
}

编辑/更新:将此代码添加到我的控制器中,为“ theResult”返回“ true”,这使我认为ContentDispositionHeaderValueIdentityExtensions正在使用其他一些缓存的请求/标头集合,因为这是它们的确切评估代码正在使用...,或者它们检索标头的方式与此不同。

var header = HttpContext.Request.GetTypedHeaders().ContentDisposition;
var theResult = header.DispositionType.Equals("form-data") && (!StringSegment.IsNullOrEmpty(header.FileName) || !StringSegment.IsNullOrEmpty(header.FileNameStar));

1 个答案:

答案 0 :(得分:0)

最终导致问题是手动发送给我们的请求是手动构建的,而不使用任何库(因为它是在发布多个这样的文件显然是一场噩梦的时代开发的)

如果您阅读了基于表单的文件提交here的规范,则会看到您还添加了一个边界(在两个文件二进制数据之间定义为一串字符,作为分隔符)直到请求结束,以显示您的最后一个文件已完全结束。

在最后一个/最后一个边界上,您必须在边界之后以“-”结尾,以表明这是上载的结尾。我们的客户代码未添加最后一组破折号。显然,这是在我们上载到的旧服务器上的IIS / Full .Net Framework中处理的,它必须是无声错误或只是假定没有更多文件并允许请求通过才有效。使用asp dotnet内核不是这种情况,并且尝试处理请求时失败。

解决方法的确是在应用程序中添加了中间件,确保在添加MVC之前将其添加到应用程序设置中。

app.RepairMissingFormDataHeaders();
app.UseCors("AllowSpecificOrigin");
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc(routes =>
{
    routes.MapRoute("default", "api/{controller}/{action=Index}/{id?}");
});

基本上,中间件读取请求的最后几个字节,如果它与“预期”字节不匹配,它将在内存中创建一个新的字节数组并将其添加到位。中间件中还有其他检查,因为只有一个客户端命中该端点,因此我们限制它在context.Request.Path上以减少运行该端点的次数

//Get a flipped version of what the final bytes should be
var endingBytes = Encoding.UTF8.GetBytes("--\r\n").Reverse().ToArray();
var requiresRepair = false;

var last4Bytes = new byte[4];
//Check if the final bites line up
for (int i = 0; i < 4; i++)
{
    last4Bytes[i] = bytes[bytes.Length - (i + 1)];
}

var lastBytesAsString = System.Text.Encoding.UTF8.GetString(last4Bytes);
Logger.LogInformation($"MIDDLEWARE: Last 4 bytes {lastBytesAsString}");

//Check if the final bites line up
for (int i = 0; i < 4; i++)
{
    if (bytes[bytes.Length - (i + 1)] != endingBytes[i])
    {
        requiresRepair = true;
        break;
    }
}