如何通过.NET在Angular与Cosmos DB之间传输无类纯JSON数据
你遇到的报错本质是JSON序列化库不兼容:
当前Cosmos DB .NET SDK默认使用Newtonsoft.Json做序列化,你调用ReadItemAsync<dynamic>返回的response.Resource实际是Newtonsoft.Json.Linq.JObject类型,而ASP.NET Core 3.0+默认使用System.Text.Json作为输出序列化器,没有内置对Newtonsoft的JObject类型的支持,因此触发了NotSupportedException异常。
你尝试使用JsonDocument失败的原因是JsonDocument没有公共无参构造函数,Cosmos SDK反序列化时无法构造该类型的实例。
不需要做任何序列化/反序列化操作,完全实现你的"透传通道"需求,性能最高,也不需要定义任何业务类:
查询数据实现
直接调用Cosmos SDK的流读取API,拿到原始JSON流后直接返回给前端:
public async Task<IActionResult> GetItemAsync(string id) { try { // 直接读取原始JSON流,不做反序列化 using var response = await _container.ReadItemStreamAsync(id, new PartitionKey(id)); if (response.StatusCode == System.Net.HttpStatusCode.NotFound) { return NotFound(); } // 直接返回流,设置正确的Content-Type return File(response.Content, "application/json"); } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { return NotFound(); } }
写入数据实现
前端提交的JSON不需要反序列化,直接读取请求体流写入Cosmos:
public async Task<IActionResult> UpsertItemAsync(string id) { using var requestStream = Request.Body; using var response = await _container.UpsertItemStreamAsync(requestStream, new PartitionKey(id)); if (response.IsSuccessStatusCode) { return NoContent(); } return StatusCode((int)response.StatusCode); }
提示:该方案会原封不动返回Cosmos DB内置的系统字段(如
_etag/_ts/_rid等),如果需要过滤这些字段,可以使用下面的备选方案。
让ASP.NET Core使用Newtonsoft.Json作为输出序列化器,兼容Cosmos SDK返回的JObject类型:
- 安装NuGet包:
Microsoft.AspNetCore.Mvc.NewtonsoftJson - 在Program.cs中注册控制器时配置:
builder.Services.AddControllers() .AddNewtonsoftJson();
配置完成后你之前用dynamic的代码可以直接正常运行。
让Cosmos SDK改用ASP.NET Core默认的System.Text.Json做序列化,避免类型不兼容问题:
- 安装NuGet包:
Microsoft.Azure.Cosmos.Serialization.SystemTextJson - 初始化CosmosClient时配置序列化器:
var cosmosClient = new CosmosClient("你的Cosmos连接串", new CosmosClientOptions { Serializer = new SystemTextJsonCosmosSerializer(new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) });
- 调整查询代码,使用
JsonElement作为泛型参数:
public async Task<IActionResult> GetItemAsync(string id) { try { var response = await _container.ReadItemAsync<JsonElement>(id, new PartitionKey(id)); return Ok(response.Resource); } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { return NotFound(); } }
内容的提问来源于stack exchange,提问作者Gotts

