web-dev-qa-db-fra.com

Les chaînes envoyées via l'API Web sont mises entre guillemets

J'ai rencontré un petit problème avec mes API Web dans ASP.NET 4, en utilisant C #. J'essaie de créer une interface graphique front-end qui envoie et reçoit des données via plusieurs API Web. La raison d'avoir plus d'une API est liée à notre réseau, qui se compose de plusieurs zones sécurisées. Les serveurs sont placés dans différentes zones et une simple requête peut devoir passer par 3 API différentes.

Plus précisément, j'envoie un objet JSON de l'interface graphique à la première API. L'objet JSON est censé être transmis à la prochaine API et à la suivante. À partir de là, un nouvel objet JSON est créé et renvoyé dans le même chemin.

Le chemin lui-même fonctionne bien. Le problème est que l'objet JSON ne peut pas être analysé une fois qu'il est retourné à l'interface graphique. Je reçois une chaîne JSON qui a été entourée de guillemets, une fois pour chaque API.

La chaîne peut commencer comme ceci:

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

Le premier saut entre les API me donne ceci:

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

Après le prochain saut, cela ressemble à ceci:

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

Et au moment où mon interface graphique le met en place, nous avons quelque chose comme ceci:

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

C’est là que l’analyse échoue pour des raisons évidentes. L'objet JSON lui-même est correctement formaté, mais tous les guillemets posent des problèmes à l'analyseur JSON.net (tous les guillemets à l'intérieur de l'objet sont également encapsulés plusieurs fois).

Ce que j’ai essayé jusqu’à présent, c’est d’envoyer la demande en tant que type application/json et type text/plain. Les deux ont fait la même chose. Les API retournent un HttpResponseMessage, qui est lu à l'aide de ReadAsStringAsync (). J'ai également essayé d'éviter la lecture de chaîne et viens de lire directement à partir de HttpRequestMessage dans un HttpResponseMessage et de faire ReadAsStringAsync () uniquement dans l'interface graphique, mais le problème persiste. La chaîne JSON est créée à l'aide de la méthode JSON.nets Serialize () et insérée dans le contenu HttpContent à l'aide de StringContent (). Cela semble faire le travail correctement. Je crois que les citations sont générées lorsque l'API et l'interface graphique reçoivent le HttpResponseMessage.

Avez-vous une idée de la façon dont je peux envoyer et recevoir la chaîne JSON en tant que chaîne brute, qui n’est traitée en aucune façon?

Je peux contourner ce problème en analysant l'objet dans un JToken ou un JObject à chaque API et en le sérialisant à nouveau. Cependant, ce n'est pas une bonne solution - je préférerais que les API transmettent simplement le message exactement comme elles l'ont reçu et ne fassent rien avec. J'ai examiné les transferts à l'aide de l'acheminement, mais il y a beaucoup d'activités liées aux autorisations, qui nécessitent que j'utilise une action API, et non une route de redirection.

Pour clarifier (TLDR): La solution idéale serait que les API transmettent simplement le message sans analyser ni lire quoi que ce soit. Tant que la source du message est autorisée, que la demande est cryptée et que l'URL demandée est valide, le message lui-même n'a pas beaucoup d'importance pour chacune des API "proxy".

25
kiwhen

Après BEAUCOUP de recherches, j'ai finalement compris celui-ci.

Tout d'abord; Je retournais le HttpResponseMessage directement; Je ne l'ai pas intentionnellement désérialisé à l'intérieur de chaque saut le long du chemin API.

En réalité, le problème était que nous utilisions un mélange de méthodes de sérialisation MVC "natives" et de méthodes JSON.net. L'un ou l'autre est acceptable, et fournit une passerelle nette de toutes les API. Si, toutefois, nous combinions des données sérialisées issues de méthodes natives et de méthodes JSON.net, les API plus loin dans la chaîne ne pourraient pas reconnaître le formatage et supposeraient à tort que le contenu devrait à nouveau être sérialisé (à l'aide de méthodes natives).

La solution consistait donc simplement à supprimer toutes les méthodes JSON.net du processus de sérialisation et nous avons obtenu les résultats escomptés.

8
kiwhen

Les guillemets et les barres obliques inverses sont ajoutés à chaque API "proxy" au fur et à mesure que la chaîne JSON est resérialisée pour chaque réponse, et non à la réception de la réponse.

Dans votre API de proxy, vous procédez probablement de la sorte (traitement des erreurs omis pour des raisons de concision):

[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);
}

Le problème ici est que l’API Web suppose par défaut qu’elle est responsable de la sérialisation de tout ce que vous lui donnez. Dans la plupart des cas d'utilisation, c'est exactement ce que vous souhaitez. Mais si votre contenu est déjà sérialisé en JSON, Web API n’a aucun moyen de le savoir; il sera heureux de re-sérialiser la chaîne, en ajoutant des guillemets et des barres obliques inverses au cours du processus.

Pour traverser une chaîne JSON sans modification, vous devez créer explicitement l'objet de contenu de la réponse (plutôt que de laisser l'API Web le créer), en veillant à définir le type de support sur afin que les clients en aval l'interprètent toujours comme du JSON ). Voici le code révisé:

[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;
}

Je suis sûr que ce qui précède peut être amélioré, mais c'est l'essentiel. Essayez-le et voyez s'il résout le problème pour vous. Notez que vous devrez appliquer ce correctif sur all les API de proxy.

40
Brian Rogers

Pour ASP.NET Core, décorez l'action avec [Produces("text/plain")].

Par exemple.

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

Ce n'est peut-être pas pour tout le monde, mais je voulais utiliser Ok () IActionResult de Microsoft.AspNetCore.Mvc au lieu de faire le HttpResponseMessage complet. Cela a également eu des résultats de chaîne entre guillemets:

"\"my string result\""

Au lieu de retourner une chaîne, j'ai renvoyé un objet avec l'attribut. Exemple de publication originale:

return Ok(myStringValue);

Ce qui a fonctionné pour moi:

return Ok(new { id = myStringValue });

Cela présentait l’avantage supplémentaire d’être un peu plus descriptif du côté réception de la demande.

1
ctc

J'ai eu le même problème avec System.Web.Http.ApiController parce que je sérialisais à la main au format JSON avant de renvoyer le résultat (JsonHelper est l'une de nos classes d'assistance à renvoyer Newtonsoft.Json.JsonConvert):

var jason = JsonHelper.SerializeToJson(result);
return Ok(jason);

Cela conduisait au même effet "\"{ ... }\"". La pré-sérialisation était nécessaire car nous avons des règles spécifiques pour le processus de sérialisation.

Heureusement, il existe un System.Web.Http.Results.JsonResult<T> capable de faire la même chose:

return new JsonResult<MyDataType>(result, JsonHelper.Settings, Encoding.UTF8, this);
0
Dee J. Doena

J'utilisais Microsoft.AspNetCore.Mvc dans mon code d'API Web qui est identique à @ctc. Par exemple.

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

Et mon code client a cessé de mettre les chaînes entre guillemets après la suppression de la ligne qui définit DefaultRequestHeaders.Accept sur application/json.

Code d'origine

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 version modifiée (suppression de la deuxième ligne de l'extrait ci-dessus)

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;

Mon hypothèse est qu'en définissant l'en-tête d'acceptation, la HttpResponseMessage tentera d'échapper au contenu en tant que paramètre d'en-tête d'acceptation, dans ce cas, application/json.

0
Shinbo

J'ai eu le même symptôme que dans le titre de ce fil, mais j'ai eu un problème différent que vous pourriez rencontrer. J'ai une colonne contenant des données JSON dans SQL Server 2016 (stockée dans le type de données SQL nvarchar recommandé). Lors de l'utilisation de l'API WEB dans le navigateur, toutes les guillemets de ma colonne JSON étaient échappés (guillemet inversé). Dans mon interface graphique javascript, je faisais un fichier JSON.parse sur le JSON résultant, mais je ne pouvais pas déréférencer les données JSON. J'ai d'abord pensé que le problème était dû aux barres obliques inverses, etc. Cependant, il s'est avéré que mon code javascript était capable de fonctionner correctement avec le code échappé. Le vrai problème était que j'essayais de déréférencer les données JSON à un niveau inférieur avant en exécutant JSON.parse sur les données de colonne.

Pour être plus concret ... imaginons qu'une ligne de données dans la base de données soit renvoyée sous forme de json (données 'application/json' renvoyées au navigateur) .. appelons myJSONdata. Toutes les données de toutes les colonnes sont renvoyées sous forme de fichier JSON, mais une colonne particulière (appelez-la myJSONcolumn) contient un objet JSON comportant plusieurs niveaux. 

Déréférencer le sous-niveau comme ceci échouera:

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

mais cela fonctionnera:

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