通过 Web API 发送的字符串用引号括起来
Posted
技术标签:
【中文标题】通过 Web API 发送的字符串用引号括起来【英文标题】:Strings sent through Web API's gets wrapped in quotes 【发布时间】:2016-02-14 09:34:56 【问题描述】:我在 ASP.NET 4 中使用 C# 的 Web API 遇到了一个小问题。我正在尝试创建一个前端 GUI,它通过多个 Web API 发送和接收数据。拥有多个 API 的原因与我们的网络有关,该网络由多个安全区域组成。服务器位于不同的区域,一个简单的请求可能需要通过多达 3 个不同的 API。
具体来说,我将一个 JSON 对象从 GUI 发送到第一个 API。 JSON 对象应该被转发到下一个 API 和下一个 - 并且从那里创建一个新的 JSON 对象并沿相同的路径返回。
路径本身运行良好。问题是 JSON 对象一旦返回 GUI 就无法解析。我收到一个用引号括起来的 JSON 字符串,每个 API 一次。
字符串的开头可能是这样的:
Hey! I am a string, or a JSON object of sorts!
API 之间的第一跳给了我这个:
"Hey! I am a string, or a JSON object of sorts!"
下一跳之后是这样的:
"\"Hey! I am a string, or a JSON object of sorts!\""
当我的 GUI 掌握它时,我们已经有了这样的东西:
"\"\\\"Hey! I am a string, or a JSON object of sorts!\\\"\""
这是解析失败的地方,原因很明显。 JSON 对象本身格式正确,但所有引号都导致 JSON.net 解析器出现问题(对象内的所有引号也被多次包装)。
到目前为止,我尝试将请求作为 application/json 类型和 text/plain 类型发送。两者都做了同样的事情。 API 返回一个 HttpResponseMessage,使用 ReadAsStringAsync() 读取。我还尝试避免读取字符串,直接从 HttpRequestMessage 读取到 HttpResponseMessage 并仅在 GUI 中执行 ReadAsStringAsync(),但问题仍然存在。 JSON 字符串是使用 JSON.nets Serialize() 方法创建的,并使用 StringContent() 放入 HttpContent。这似乎正确地完成了这项工作。我相信引号是在 API 和 GUI 收到 HttpResponseMessage 时生成的。
知道如何将 JSON 字符串作为原始字符串发送和接收,不以任何方式处理吗?
我可以通过在每个 API 上将对象解析为 JToken 或 JObject 并再次对其进行序列化来绕过此行为。然而,这不是一个好的解决方案——我宁愿 API 只是按照他们得到的方式转发消息,而不是对它做任何事情。我使用路由研究了转发,但是有很多授权的东西,这确实需要我使用 API 操作,而不是重定向路由。
澄清 (TLDR):理想的解决方案是让 API 简单地传递消息而不解析或读取任何内容。只要消息的来源被授权,请求被加密并且请求的 URL 是有效的,消息本身对每个“代理”API 并不重要。
【问题讨论】:
您能否提供一个代码示例来演示您的问题的Minimal, Complete, and Verifiable example?如果我搜索“asp.net json double serialize”,我会找到 WCF RESTful web service with JSON.Net: avoid double serialization 和 JSON.NET Parser seems to be double serializing my objects 以及 ASP.NET web services mistake: manual JSON serialization。 【参考方案1】:在每个“代理”API 中添加引号和反斜杠,因为 JSON 字符串会针对每个响应进行重新序列化,而不是在收到响应时。
在您的代理 API 中,大概您正在做这样的事情(为简洁起见,省略了错误处理):
[HttpGet]
public async Task<HttpResponseMessage> GetWidget(int id)
HttpClient client = new HttpClient();
string url = "http://nextapiserver.example.org/widgets/" + id;
string json = await client.GetStringAsync(url);
return Request.CreateResponse(HttpStatusCode.OK, json);
这里的问题是 Web API 默认假设它负责序列化你给它的任何东西。对于大多数用例,这正是您想要的。但如果您的内容已经序列化为 JSON,Web API 无法知道这一点;它会愉快地重新序列化字符串,在此过程中添加额外的引号和反斜杠。
要传递一个不受影响的 JSON 字符串,您需要显式创建响应内容对象(而不是让 Web API 创建它),确保将媒体类型设置为以便下游客户端仍将其解释为 JSON(而不是比纯文本)。这是修改后的代码:
[HttpGet]
public async Task<HttpResponseMessage> GetWidget(int id)
HttpClient client = new HttpClient();
string url = "http://nextapiserver.example.org/widgets/" + id;
string json = await client.GetStringAsync(url);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
我确信以上内容可以改进,但这就是它的要点。试一试,看看它是否可以为您解决问题。请注意,您需要在所有代理 API 上应用此修复。
【讨论】:
这很好用。不知道为什么它不是正确的答案。只需返回一个字符串。 谢谢,应该是这个答案 我有类似的工作: var response = await _httpClient.GetAsync($"_apiBaseAddress/api/Endpoint"); if (response.StatusCode == HttpStatusCode.OK) var content = await response.Content.ReadAsStringAsync();字符串 toRet = JsonConvert.DeserializeObject(内容);返回Ret;基本上,当我添加“JsonConvert.DeserializeObject对于 ASP.NET Core,使用 [Produces("text/plain")]
装饰操作。
例如。
[HttpGet("/"), Produces("text/plain")]
public IActionResult HelloWorld() => Ok("Hello World");
【讨论】:
谢谢。这有助于我返回序列化数据表并将其反序列化。结果是添加了额外的反斜杠。【参考方案3】:经过大量研究,我终于弄清楚了这一点。
首先;我直接返回 HttpResponseMessage;我不是故意在 API 路径的每个跃点内对其进行反序列化。
事实证明,问题实际上是我们混合使用了“本机”MVC 序列化方法和 JSON.net 的方法。任何一个本身都很好,并且提供了所有 API 的干净传递。但是,如果我们将来自本机方法和 JSON.net 方法的序列化数据结合起来,则更下游的 API 将无法识别格式并错误地假设内容应该再次序列化(使用本机方法)。
所以解决方案只是从序列化过程中删除所有 JSON.net 方法,我们最终得到了预期的结果。
【讨论】:
【参考方案4】:这可能不适合所有人,但我想使用 Microsoft.AspNetCore.Mvc 中的 Ok() IActionResult 而不是完整的 HttpResponseMessage。这也有引号中的字符串结果:
"\"my string result\""
我没有返回一个字符串,而是返回了一个具有该属性的对象。原始问题示例:
return Ok(myStringValue);
什么对我有用:
return Ok(new id = myStringValue );
这有一个额外的好处,那就是在请求的接收方更具描述性。
【讨论】:
【参考方案5】:我遇到了这个问题,所以我通过 Nuget Packages Manager 安装了Newtonsoft.Json
。然后反序列化您的 JSON 字符串:
string deserializedString = JsonConvert.DeserializeObject<string>(yourJsonString);
不要忘记导入命名空间:
using Newtonsoft.Json;
【讨论】:
【参考方案6】:我的症状与该主题的标题相同,但最终遇到了您可能遇到的不同问题。我在 SQL Server 2016 中有一个包含 JSON 数据的列(存储在推荐的 nvarchar SQL 数据类型中)。在浏览器中点击 WEB API 时,我的 JSON 列中的所有双引号都被转义(反斜杠引号)。在我的 javascript GUI 中,我正在对生成的 JSON 执行 JSON.parse,但无法取消引用 JSON 数据。起初我认为问题在于反斜杠等。但是,事实证明我的 javascript 代码能够很好地处理转义代码。真正的问题是我试图在对列数据运行 JSON.parse 之前在子级别取消引用 JSON 数据。
更具体地说...假设数据库中的一行数据作为 json 返回('application/json' 数据返回给浏览器)..lets 调用是 myJSONdata。所有列的所有数据都以 JSON 形式返回,但是一个特定的列(称为 myJSONcolumn)有一个深度为几个级别的 JSON 对象。
像这样取消对子级别的引用会失败:
JSON.parse(myJSONdata["myJSONcolumn"]["sublevel1"])
但这会起作用:
JSON.parse(myJSONdata["myJSONcolumn"])["sublevel1"]
【讨论】:
【参考方案7】:我在与@ctc 相同的Web API 代码中使用Microsoft.AspNetCore.Mvc
。例如
var myStringValue = "string value";
return Ok(myStringValue);
并且我的客户端代码在删除将DefaultRequestHeaders.Accept
设置为application/json
的行后停止将字符串用引号括起来。
原始代码
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var request = new HttpRequestMessage(HttpMethod.Post, "http://<Host>/<Path>");
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var httpResponseMessage = client.SendAsync(request).Result;
var responseString = httpResponseMessage.Content.ReadAsStringAsync().Result;
vs修改版(去掉上面sn-p中的第二行)
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "http://<Host>/<Path>");
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var httpResponseMessage = client.SendAsync(request).Result;
var responseString = httpResponseMessage.Content.ReadAsStringAsync().Result;
我的猜测是,通过设置接受标头,HttpResponseMessage
将尝试将内容转义为接受标头设置,在本例中为application/json
。
【讨论】:
【参考方案8】:System.Web.Http.ApiController
有同样的问题,因为我在返回结果之前手动序列化为 JSON(JsonHelper
是我们用来包装Newtonsoft.Json.JsonConvert
的辅助类):
var jason = JsonHelper.SerializeToJson(result);
return Ok(jason);
这导致了相同的"\" ... \""
效果。预序列化是必要的,因为我们对序列化过程有一些特定的规则。
幸运的是有一个System.Web.Http.Results.JsonResult<T>
可以做同样的事情:
return new JsonResult<MyDataType>(result, JsonHelper.Settings, Encoding.UTF8, this);
【讨论】:
【参考方案9】:您可以使用GetAsync
代替GetStringAsync
public async Task<HttpResponseMessage> Get(int id)
using (HttpClient httpClient = new HttpClient())
return await httpClient.GetAsync(url);
处理验证和响应代码。
【讨论】:
以上是关于通过 Web API 发送的字符串用引号括起来的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Json.Net 从我的 WCF 休息服务(.NET 4)返回 json,而不是用引号括起来的字符串?