Строки, отправленные через Web API, завершаются в кавычки

Я столкнулся с небольшой проблемой с моим веб-API в ASP.NET 4, используя С#. Я пытаюсь создать интерфейс GUI, который отправляет и получает данные через несколько веб-API. Причина наличия нескольких API связана с нашей сетью, которая состоит из нескольких безопасных зон. Серверы расположены в разных зонах, и простой запрос может проходить через 3 разных API.

В частности, я отправляю объект JSON из графического интерфейса в первый API. Предполагается, что объект JSON будет перенаправлен следующему API и следующему - и оттуда будет создан новый объект JSON, который будет возвращен по тому же пути.

Сам путь работает нормально. Проблема состоит в том, что объект JSON не может быть проанализирован, как только он возвращается в графический интерфейс. Я получаю строку JSON, заключенную в кавычки, по одному разу для каждого API.

Строка может начинаться так:

Hey! я am a string, or a JSON object of sorts!

Первый переход между API дает мне это:

"Hey! я am a string, or a JSON object of sorts!"

После следующего прыжка это выглядит так:

"\"Hey! я am a string, or a JSON object of sorts!\""

И к тому времени, когда мой GUI овладеет им, у нас будет что-то вроде этого:

"\"\\\"Hey! я am a string, or a JSON object of sorts!\\\"\""

Это где анализ не удается, по понятным причинам. Сам объект JSON правильно отформатирован, но все кавычки вызывают проблемы для синтаксического анализатора JSON.net (все кавычки внутри объекта также переносятся несколько раз).

До сих пор я пытался отправить запрос как тип application/json, так и text/plain тип. Оба сделали то же самое. API возвращает HttpResponseMessage, которые читаются с использованием ReadAsStringAsync(). Я также пытался избежать чтения строк и просто читал непосредственно из HttpRequestMessage в HttpResponseMessage и выполнял ReadAsStringAsync() только в графическом интерфейсе, но проблема все еще остается. Строка JSON создается с использованием JSON.nets Serialize() -method и помещается в HttpContent с помощью StringContent(). Это, кажется, делает работу правильно. Я считаю, что кавычки генерируются, когда API и GUI получает HttpResponseMessage.

Любая идея, как я могу отправить и получить строку JSON в виде необработанной строки, которая не рассматривается в любом случае?

Я могу обойти это поведение, анализируя объект в JToken или JObject в каждом API и снова сериализуя его. Однако это не очень хорошее решение - я бы предпочел, чтобы API просто отправлял сообщение именно так, как они его получили, и ничего с ним не делал. Я смотрел вперед, используя маршрутизацию, но там происходит много вещей авторизации, которые требуют, чтобы я использовал действие API, а не маршрут перенаправления.

Чтобы уточнить (TL;DR): Идеальным решением было бы для API просто передать сообщение без разбора или чтения чего-либо в него. Пока источник сообщения авторизован, запрос зашифрован и запрошенный URL-адрес действителен, само сообщение не имеет большого значения для каждого из "прокси" API.

Ответы

Ответ 1

После MUCH исследований я, наконец, понял это.

Прежде всего; Я возвращал HttpResponseMessage напрямую; Я не намеренно десериализую его внутри каждого прыжка по API-пути.

Проблема, как оказалось, заключалась в том, что мы использовали сочетание "родных" методов сериализации MVC и методов JSON.net. Один из них сам по себе хорош и обеспечивает чистую сквозную передачу всех API. Однако, если бы мы объединили сериализованные данные как из собственных методов, так и из JSON.net-методов, API дальше по цепочке не смог бы распознать форматирование и неправильно предположить, что контент должен быть снова сериализован (используя собственные методы).

Таким образом, решение было просто удалить все методы JSON.net из процесса сериализации, и мы получили ожидаемые результаты.

Ответ 2

Кавычки и обратные косые черты добавляются в каждый "прокси" 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, веб-API не знает об этом; он будет счастливо повторно сериализовать строку, добавив дополнительные кавычки и обратную косую черту в процессе.

Чтобы передать строку JSON нетронутой, вам необходимо явно создать объект содержимого ответа (вместо того, чтобы позволить веб-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.

Ответ 3

Для ASP.NET Core украсьте действие с помощью [Produces("text/plain")].

Например.

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

Ответ 4

У меня был тот же самый симптом, что и в названии этой темы, но у меня возникла другая проблема, с которой вы можете столкнуться. У меня есть столбец, содержащий данные JSON в SQL Server 2016 (хранится в рекомендованном типе dvarchar SQL). При попадании WEB API в браузер все двойные кавычки в моем столбце JSON были экранированы (обратная косая черта). В моем графическом интерфейсе javascript я делал JSON.parse в результате JSON и не мог разыменовать данные JSON. Сначала я понял, что проблема связана с обратными косыми чертами и т.д. Однако оказалось, что мой код javascript смог работать с экранированным кодом просто отлично. Реальная проблема заключалась в том, что я пытался разыменовать данные JSON на подуровне до запуска JSON.parse по данным столбца.

Чтобы быть более конкретным... представьте, что одна строка данных в db возвращается как json (данные "application/json" возвращены в браузер). Позволяет вызывать myJSONdata. Все данные для всех столбцов возвращаются как JSON, но один конкретный столбец (назовем его myJSONcolumn) имеет объект JSON, который имеет несколько уровней.

Разыменование такого подуровня не будет выполнено:

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

но это будет работать:

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

Ответ 5

Это может быть не для всех, но я хотел использовать Ok() IActionResult от Microsoft.AspNetCore.Mvc вместо того, чтобы делать полный HttpResponseMessage. Это также приводило к строковым результатам в кавычках:

"\"my string result\""

Вместо возврата строки я вернул объект с атрибутом. Пример оригинальной проблемы:

return Ok(myStringValue);

Что сработало для меня:

return Ok(new { id = myStringValue });

Это имело дополнительное преимущество, заключающееся в том, что он был немного более описательным для принимающей стороны запроса.

Ответ 6

Я использовал Microsoft.AspNetCore.Mvc в своем коде веб-API, который совпадает с @ctc. Например

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 измененная версия (удалена вторая строка в приведенном выше фрагменте)

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;

Я предполагаю, что, установив заголовок accept, HttpResponseMessage попытается экранировать содержимое в качестве настройки заголовка accept, в данном случае application/json.

Ответ 7

У меня была такая же проблема с System.Web.Http.ApiController потому что я JsonHelper сериализацию в 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);

Ответ 8

У меня была эта проблема, поэтому я установил Newtonsoft.Json через Nuget Packages Manager. Затем для десериализации вашей строки JSON:

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

Не забудьте импортировать пространство имен:

using Newtonsoft.Json;

Ответ 9

Вы можете использовать GetAsync вместо GetStringAsync

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

для обработки кодов проверки и ответа.