You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET API:如何拒绝大Multipart请求并返回401且不关闭连接?

问题描述

客户端大文件上传代码

var content = new MultipartFormDataContent();
var stream = new FileStream("c:\\10GB.zip", FileMode.Open, FileAccess.Read);
content.Add(new StreamContent(stream), "file", fileInfo.Name);
request.Content = content;
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
   return Outcome<string>.Success($"{response.StatusCode} - {await response.Content.ReadAsStringAsync()}");

var result = await response.Content.ReadAsStringAsync();
return Outcome<string>.Fail($"Failed to upload file: {response.StatusCode} - {result}");

API Token验证中间件代码

context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{ \"error\": \"no token found\" }");

当前遇到的问题:

  • 中间件通过不调用后续中间件终止请求时,TCP连接会被关闭,客户端抛出HttpRequestException,无法正常获取401状态码和JSON响应体。
  • 询问该行为是否存在问题,以及当请求体为大文件流时,如何可靠地拒绝请求并让客户端明确知晓已被拒绝。已尝试关闭流但连接仍会终止,读取整个大文件流显然不是可行方案。

解决方案

1. 问题根源

你当前的处理方式存在两个核心问题:

  • 响应内容未完全发送就终止请求,导致TCP连接被强制断开,HttpClient会将这种情况判定为网络异常,而非正常的HTTP错误响应。
  • 未处理请求体的丢弃逻辑:服务端返回错误响应时,需要告知客户端停止发送剩余请求体,同时自身也要安全丢弃已接收但未处理的请求数据(无需读取完整大文件)。

2. 修正后的中间件代码

修改Token验证中间件,按以下逻辑处理:

// 设置401状态码与响应类型
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";

// 写入错误响应内容
var errorJson = "{ \"error\": \"no token found\" }";
await context.Response.WriteAsync(errorJson);
// 确保响应内容完全发送到客户端
await context.Response.CompleteAsync();

// 关键:安全丢弃剩余请求体,避免资源占用
if (context.Request.ContentLength > 0 || !context.Request.Body.CanSeek)
{
    // 告知客户端停止发送数据
    context.Response.Headers.TryAdd("Transfer-Encoding", "chunked");
    // 直接丢弃请求体流,无需读取全部内容
    await context.Request.Body.DrainAsync(CancellationToken.None);
}

// 终止后续中间件执行
return;

3. 客户端异常兼容处理

HttpClient默认会在连接异常时抛出HttpRequestException,可以通过捕获异常并尝试提取响应信息来兼容:

try
{
    var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        return Outcome<string>.Success($"{response.StatusCode} - {responseContent}");
    }
    else
    {
        var errorContent = await response.Content.ReadAsStringAsync();
        return Outcome<string>.Fail($"Failed to upload file: {response.StatusCode} - {errorContent}");
    }
}
catch (HttpRequestException ex)
{
    // 尝试从异常中提取服务端返回的响应
    if (ex.InnerException is HttpProtocolException protocolEx && protocolEx.Response != null)
    {
        var errorContent = await protocolEx.Response.Content.ReadAsStringAsync();
        return Outcome<string>.Fail($"Request rejected: {protocolEx.Response.StatusCode} - {errorContent}");
    }
    // 处理其他网络异常情况
    return Outcome<string>.Fail($"Network error occurred: {ex.Message}");
}

4. 额外优化建议

  • 提前验证权限:客户端可以在发起大文件上传前,先发送一个轻量的Token验证请求(比如HEAD请求),提前确认权限,避免大文件传输到一半被拒绝。
  • 尽早验证Token:服务端中间件尽量在请求体开始读取前完成Token验证(比如在HttpContext.OnStarting回调中),可以更早触发拒绝逻辑,减少不必要的网络带宽消耗。

内容的提问来源于stack exchange,提问作者Jonas Rembratt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 15:15:59