web-dev-qa-db-fra.com

MVC: comment renvoyer une chaîne au format JSON

Dans le but de rendre le processus de rapport d'avancement un peu plus fiable et de le dissocier de la demande/réponse, je suis en train d'effectuer le traitement dans un service Windows et de conserver la réponse souhaitée à un fichier. Lorsque le client commence à interroger les mises à jour, le contrôleur renvoie le contenu du fichier, quel qu'il soit, sous forme de chaîne JSON.

Le contenu du fichier est pré-sérialisé en JSON. Cela permet de s’assurer que rien ne fait obstacle à la réponse. Aucun traitement n'est nécessaire (à moins de lire le contenu du fichier dans une chaîne et de le renvoyer) pour obtenir la réponse.

Au départ, je pensais que cela serait assez simple, mais ce n’est pas le cas.

Actuellement, ma méthode de contrôleur ressemble à ceci:

Manette

Mis à jour

[HttpPost]
public JsonResult UpdateBatchSearchMembers()
{
    string path = Properties.Settings.Default.ResponsePath;
    string returntext;
    if (!System.IO.File.Exists(path))
        returntext = Properties.Settings.Default.EmptyBatchSearchUpdate;
    else
        returntext = System.IO.File.ReadAllText(path);

    return this.Json(returntext);
}

Et Fiddler renvoie ceci comme réponse brute

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 19 Mar 2012 20:30:05 GMT
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 3.0
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 81
Connection: Close

"{\"StopPolling\":false,\"BatchSearchProgressReports\":[],\"MemberStatuses\":[]}"

AJAX

Mis à jour

Les éléments suivants seront probablement modifiés ultérieurement, mais pour l'instant, cela fonctionnait lorsque je générais la classe de réponse et que je le retournais sous forme de code JSON, comme une personne normale.

this.CheckForUpdate = function () {
var parent = this;

if (this.BatchSearchId != null && WorkflowState.SelectedSearchList != "") {
    showAjaxLoader = false;
    if (progressPending != true) {
        progressPending = true;
        $.ajax({
            url: WorkflowState.UpdateBatchLink + "?SearchListID=" + WorkflowState.SelectedSearchList,
            type: 'POST',
            contentType: 'application/json; charset=utf-8',
            cache: false,
            success: function (data) {
                for (var i = 0; i < data.MemberStatuses.length; i++) {
                    var response = data.MemberStatuses[i];
                    parent.UpdateCellStatus(response);
                }
                if (data.StopPolling = true) {
                    parent.StopPullingForUpdates();
                }
                showAjaxLoader = true;
            }
        });
        progressPending = false;
    }
}
60
CodeWarrior

Le problème, à mon avis, est que le résultat de l'action Json est destiné à prendre un objet (votre modèle) et à créer une réponse HTTP avec un contenu sous la forme de données au format JSON à partir de votre objet modèle.

Ce que vous transmettez à la méthode Json du contrôleur, cependant, est un objet string au format JSON. Il s'agit donc de "sérialiser" l'objet string en JSON. citations (je suppose que c'est le problème).

Je pense que vous pouvez envisager d'utiliser le résultat de l'action Content comme alternative au résultat de l'action Json, car vous disposez déjà du contenu brut pour la réponse HTTP.

return this.Content(returntext, "application/json");
// not sure off-hand if you should also specify "charset=utf-8" here, 
//  or if that is done automatically

Une autre solution consisterait à désérialiser le résultat JSON du service en un objet, puis à le transmettre à la méthode Json du contrôleur, mais le désavantage serait que vous désérialiseriez puis resérialiseriez les données, ce qui peut être inutile. pour vos buts.

107

Vous devez simplement renvoyer ContentResult standard et définir ContentType sur "application/json" . Vous pouvez créer un ActionResult personnalisé pour celui-ci:

public class JsonStringResult : ContentResult
{
    public JsonStringResult(string json)
    {
        Content = json;
        ContentType = "application/json";
    }
}

Et puis retourne son exemple:

[HttpPost]
public JsonResult UpdateBatchSearchMembers()
{
    string returntext;
    if (!System.IO.File.Exists(path))
        returntext = Properties.Settings.Default.EmptyBatchSearchUpdate;
    else
        returntext = Properties.Settings.Default.ResponsePath;

    return new JsonStringResult(returntext);
}
39
Dmitriy Startsev

Ouais c'est tout, sans autre problème, pour éviter la chaîne brute json c'est ça.

    public ActionResult GetJson()
    {
        var json = System.IO.File.ReadAllText(
            Server.MapPath(@"~/App_Data/content.json"));

        return new ContentResult
        {
            Content = json,
            ContentType = "application/json",
            ContentEncoding = Encoding.UTF8
        };
    } 

REMARQUE: notez que le type de retour de méthode JsonResult ne fonctionne pas pour moi, car JsonResult et ContentResult héritent tous deux de ActionResult mais il n'y a pas de relation entre eux.

Toutes les réponses ici fournissent un bon code et qui fonctionne. Mais quelqu'un serait mécontent du fait qu'ils utilisent tous ContentType comme type de retour et non JsonResult

Malheureusement, JsonResult utilise JavaScriptSerializer sans option pour le désactiver. Le meilleur moyen de contourner ce problème est d’hériter de JsonResult.

J'ai copié la plupart du code de l'original JsonResult et créé la classe JsonStringResult qui renvoie la chaîne passée sous la forme application/json. Le code pour cette classe est ci-dessous

public class JsonStringResult : JsonResult
    {
        public JsonStringResult(string data)
        {
            JsonRequestBehavior = JsonRequestBehavior.DenyGet;
            Data = data;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
                String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("Get request is not allowed!");
            }

            HttpResponseBase response = context.HttpContext.Response;

            if (!String.IsNullOrEmpty(ContentType))
            {
                response.ContentType = ContentType;
            }
            else
            {
                response.ContentType = "application/json";
            }
            if (ContentEncoding != null)
            {
                response.ContentEncoding = ContentEncoding;
            }
            if (Data != null)
            {
                response.Write(Data);
            }
        }
    }

Exemple d'utilisation:

var json = JsonConvert.SerializeObject(data);
return new JsonStringResult(json);
0
Shoter

Utilisez le code suivant sur votre contrôleur:

return Json(new { success = string }, JsonRequestBehavior.AllowGet);

et en JavaScript:

success: function (data) {
    var response = data.success;
    ....
}
0
user3652935