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

C# REST PATCH接口JSON体传递Stream报错的解决方法

C# PATCH请求含Stream属性的DTO调用失败解决方法

问题描述

我在C#中实现了一个REST MVC控制器的PATCH方法,请求体对应的DTO包含普通属性和Stream类型的文件属性:

public class MyDTO
{
    public string SomeProperty { get; set; }
    public Stream SomeFile { get; set; }
}

Swagger UI生成的示例JSON将SomeFile显示为字符串,客户端调用时,无论是赋值FileStream还是内存流,都会抛出错误:

Timeouts are not supported on this stream

对应的堆栈跟踪:

System.Net.Http.HttpRequestException : An error occurred while sending the request.
----> System.InvalidOperationException : Timeouts are not supported on this stream.
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at ServiceClient.Classes.RestClient.SendPatchRequestNoAuth[TContentType](String url, TContentType content) in C:\Development\ServiceClient\Classes\RestClient.cs:line 58

解决方案

1. 改用Multipart/form-data传输(推荐)

JSON序列化器无法原生处理Stream类型,这是问题根源。正确的做法是用multipart/form-data格式混合传输普通属性和文件:

服务端调整

修改控制器方法,用[FromForm]接收表单数据,DTO改用IFormFile接收文件:

[HttpPatch]
public async Task<IActionResult> PatchResource([FromForm] MyFormDto dto)
{
    // 读取文件内容示例
    using var reader = new StreamReader(dto.SomeFile.OpenReadStream());
    var fileContent = await reader.ReadToEndAsync();
    
    // 业务逻辑处理
    return Ok();
}

public class MyFormDto
{
    public string SomeProperty { get; set; }
    public IFormFile SomeFile { get; set; }
}

客户端调整

构建MultipartFormDataContent来组装请求内容:

public async Task SendPatchWithFile(string apiUrl, string propertyValue, Stream fileStream, string fileName)
{
    using var formContent = new MultipartFormDataContent();
    // 添加普通字符串属性
    formContent.Add(new StringContent(propertyValue), "SomeProperty");
    
    // 添加文件流,指定媒体类型
    using var fileContent = new StreamContent(fileStream);
    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
    formContent.Add(fileContent, "SomeFile", fileName);

    using var httpClient = new HttpClient();
    var response = await httpClient.PatchAsync(apiUrl, formContent);
    response.EnsureSuccessStatusCode();
}

2. 转Base64字符串传输(仅适合小文件)

如果必须用JSON格式,可以将Stream转换为Base64字符串:

修改DTO

public class MyDto
{
    public string SomeProperty { get; set; }
    public string SomeFileBase64 { get; set; } // 替换Stream为Base64字符串
}

客户端处理

将文件流转为Base64后序列化JSON:

// 把文件流复制到内存流再转Base64
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
var fileBase64 = Convert.ToBase64String(memoryStream.ToArray());

var dto = new MyDto 
{ 
    SomeProperty = "测试值", 
    SomeFileBase64 = fileBase64 
};

// 发送JSON格式的PATCH请求
using var httpClient = new HttpClient();
var jsonContent = JsonContent.Create(dto);
var response = await httpClient.PatchAsync(apiUrl, jsonContent);
response.EnsureSuccessStatusCode();

服务端还原

[HttpPatch]
public IActionResult PatchResource([FromBody] MyDto dto)
{
    // 将Base64转回Stream
    var fileBytes = Convert.FromBase64String(dto.SomeFileBase64);
    var fileStream = new MemoryStream(fileBytes);
    
    // 业务逻辑处理
    return Ok();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:02:18