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

时间:2015-11-12 21:41:21

标签: c# json api asp.net-web-api

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

具体来说,我将GUI中的JSON对象发送到第一个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' S

9 个答案:

答案 0 :(得分:44)

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

在你的代理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上应用此修复程序。

答案 1 :(得分:8)

经过多次研究,我终于想出了这个。

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

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

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

答案 2 :(得分:4)

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

例如。

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

答案 3 :(得分:1)

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

更具体一点......假设数据库中的一行数据以json('application / json'数据返回给浏览器)的形式返回..让调用是myJSONdata。所有列的所有数据都以JSON形式返回,但是一个特定列(称为myJSONcolumn)具有多个级别的JSON对象。

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

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

但这会奏效:

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

答案 4 :(得分:1)

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

"\"my string result\""

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

return Ok(myStringValue);

对我有用的是什么:

return Ok(new { id = myStringValue });

这样做的另一个好处是在请求的接收方更具描述性。

答案 5 :(得分:0)

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

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://<Host>/<Path>");
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var httpResponseMessage = client.SendAsync(request).Result;
var responseString = httpResponseMessage.Content.ReadAsStringAsync().Result;

相对于修改后的版本(删除了以上代码段中的第二行)

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

答案 6 :(得分:0)

我在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);

答案 7 :(得分:0)

我遇到了这个问题,因此我通过Nuget Packages Manager安装了Newtonsoft.Json。然后反序列化您的JSON字符串:

string deserializedString = JsonConvert.DeserializeObject<string>(yourJsonString);

别忘了导入名称空间:

using Newtonsoft.Json;

答案 8 :(得分:-1)

您可以使用GetAsync而不是GetStringAsync:

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

处理验证和响应代码。