通过Web API发送的字符串包含在引号中

我使用C#在ASP.NET 4中使用我的Web API遇到了一个小问题。 我正在尝试创建一个前端GUI,它通过多个Web API发送和接收数据。 拥有多个API的原因与我们的网络有关,该网络由多个安全区域组成。 服务器位于不同的区域,简单的请求可能需要通过3个不同的API。

具体来说,我正在从GUI向第一个API发送一个JSON对象。 应该将JSON对象转发到下一个API,然后从那里创建一个新的JSON对象,并在同一路径下返回。

路径本身很好用。 问题是JSON对象一旦返回GUI就无法解析。 我收到一个用引号括起来的JSON字符串,每个API一次。

字符串可能会像这样开始:

嘿! 我是一个字符串,或各种JSON对象!

API之间的第一跳给了我:

“嘿!我是一个字符串,或者是各种各样的JSON对象!”

在下一跳之后它看起来像这样:

“\“嘿! 我是一个字符串,或者是各种各样的JSON对象!\“”

当我的GUI掌握它时,我们有这样的事情:

“\”\“”嘿!我是一个字符串,或者是各种各样的JSON对象!\\“\”“

这是解析失败的原因,原因很明显。 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都无关紧要。

每个“代理”API都会添加引号和反斜杠,因为每个响应都会重新序列化JSON字符串,而不是在收到响应时。

在您的代理API中,可能您正在执行类似的操作(为简洁起见,省略了error handling):

[HttpGet] public async Task 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 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上应用此修复程序。

经过多方面的研究,我终于想出了这个。

首先; 我直接返回了HttpResponseMessage; 我没有故意在API路径的每一跳内反序列化它。

事实certificate,问题实际上是我们使用了“本机”MVC序列化方法和JSON.net方法的混合。 这两者本身都很好,并提供所有API的干净通道。 但是,如果我们将来自本机方法和JSON.net方法的序列化数据组合在一起,那么链下游的API将无法识别格式并错误地认为应该再次序列化内容(使用本机方法)。

所以解决方案只是从序列化过程中删除所有JSON.net方法,我们最终得到了预期的结果。

这可能不适合所有人,但我想使用Microsoft.AspNetCore.Mvc中的Ok()IActionResult而不是完整的HttpResponseMessage。 这也有引号的字符串结果:

 "\"my string result\"" 

我返回了一个带有该属性的对象,而不是返回一个字符串。 原始问题示例:

 return Ok(myStringValue); 

什么对我有用:

 return Ok(new { id = myStringValue }); 

这具有额外的好处,即在请求的接收方更具描述性。

对于ASP.NET Core,使用[Produces("text/plain")]装饰动作。

例如。

 [HttpGet("/"), Produces("text/plain")] public IActionResult HelloWorld() => Ok("Hello World"); 

我有与此主题标题相同的症状但最终遇到了您可能遇到的其他问题。 我在SQL Server 2016中有一个包含JSON数据的列(存储在推荐的nvarchar SQL数据类型中)。 在浏览器中点击WEB API时,我的JSON列中的所有双引号都被转义(反斜杠引用)。 在我的javascript GUI中,我在生成的JSON上执行了JSON.parse,并且无法取消引用JSON数据。 我一开始认为问题是反斜杠等。然而,事实certificate我的javascript代码能够使用转义代码就好了。 真正的问题是我在列数据上运行JSON.parse 之前尝试在子级别取消引用JSON数据。

更具体一点……想象一下db中的一行数据是以json(’application / json’数据返回给浏览器)的forms返回的..让我们调用myJSONdata。 所有列的所有数据都以JSONforms返回,但是一个特定的列(称为myJSONcolumn)具有多个级别的JSON对象。

像这样取消引用子级将失败:

 JSON.parse(myJSONdata["myJSONcolumn"]["sublevel1"]) 

但这会奏效:

 JSON.parse(myJSONdata["myJSONcolumn"])["sublevel1"] 

我在我的Web API代码中使用了Microsoft.AspNetCore.Mvc ,它与@ctc相同。 例如

 var myStringValue = "string value"; return Ok(myStringValue); 

在删除行集DefaultRequestHeaders.Acceptapplication/json之后,我的客户端代码停止将字符串包装在引号中。

原始代码

 var client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var request = new HttpRequestMessage(HttpMethod.Post, "http:///"); request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); var httpResponseMessage = client.SendAsync(request).Result; var responseString = httpResponseMessage.Content.ReadAsStringAsync().Result; 

vs修改版本(删除上面代码段中的第二行)

 var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Post, "http:///"); request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); var httpResponseMessage = client.SendAsync(request).Result; var responseString = httpResponseMessage.Content.ReadAsStringAsync().Result; 

我的猜测是,通过设置accept头, HttpResponseMessage将尝试将内容转义为accept头设置,在本例中为application/json

您可以使用GetAsync而不是GetStringAsync:

 public async Task Get(int id) { using (HttpClient httpClient = new HttpClient()) { return await httpClient.GetAsync(url); } } 

处理validation和响应代码。