我有一个旧的.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));
答案 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;
}
}