web-dev-qa-db-fra.com

ASP.net MVC renvoyant JSONP

Je cherche à renvoyer du JSON à travers les domaines et je comprends que la façon de le faire est via JSONP plutôt que JSON pur. J'utilise ASP.net MVC, donc je pensais simplement étendre le type JSONResult, puis étendre Controller pour qu'il implémente également une méthode Jsonp. Est-ce la meilleure façon de procéder ou existe-t-il un ActionResult intégré qui pourrait être mieux?

Edit: je suis allé de l'avant et je l'ai fait. Juste pour référence, j'ai ajouté un nouveau résultat:

public class JsonpResult : System.Web.Mvc.JsonResult
    {
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            HttpResponseBase response = context.HttpContext.Response;

            if (!String.IsNullOrEmpty(ContentType))
            {
                response.ContentType = ContentType;
            }
            else
            {
                response.ContentType = "application/javascript";
            }
            if (ContentEncoding != null)
            {
                response.ContentEncoding = ContentEncoding;
            }
            if (Data != null)
            {
                // The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1
#pragma warning disable 0618
                HttpRequestBase request = context.HttpContext.Request;

                JavaScriptSerializer serializer = new JavaScriptSerializer();
                response.Write(request.Params["jsoncallback"] + "(" + serializer.Serialize(Data) + ")");
#pragma warning restore 0618
            }
        }
    }

et aussi quelques méthodes pour une superclasse de tous mes contrôleurs:

protected internal JsonpResult Jsonp(object data)
        {
            return Jsonp(data, null /* contentType */);
        }

        protected internal JsonpResult Jsonp(object data, string contentType)
        {
            return Jsonp(data, contentType, null);
        }

        protected internal virtual JsonpResult Jsonp(object data, string contentType, Encoding contentEncoding)
        {
            return new JsonpResult
            {
                Data = data,
                ContentType = contentType,
                ContentEncoding = contentEncoding
            };
        }

Fonctionne comme un charme.

71
stimms

Voici une solution simple, si vous ne souhaitez pas définir de filtre d'action

Code côté client utilisant jQuery:

  $.ajax("http://www.myserver.com/Home/JsonpCall", { dataType: "jsonp" }).done(function (result) {});

Action du contrôleur MVC. Renvoie le résultat du contenu avec le code JavaScript exécutant la fonction de rappel fournie avec la chaîne de requête. Définit également le type JavaScript MIME pour la réponse.

 public ContentResult JsonpCall(string callback)
 {
      return Content(String.Format("{0}({1});",
          callback, 
          new JavaScriptSerializer().Serialize(new { a = 1 })),    
          "application/javascript");
 }
16
Maksym Kozlenko

Plutôt que de sous-classer mes contrôleurs avec les méthodes Jsonp (), j'ai opté pour la méthode d'extension car cela me semble plus propre. La bonne chose à propos du JsonpResult est que vous pouvez le tester exactement de la même manière que vous le feriez avec un JsonResult.

J'ai fait:

public static class JsonResultExtensions
{
    public static JsonpResult ToJsonp(this JsonResult json)
    {
        return new JsonpResult { ContentEncoding = json.ContentEncoding, ContentType = json.ContentType, Data = json.Data, JsonRequestBehavior = json.JsonRequestBehavior};
    }
}

De cette façon, vous n'avez pas à vous soucier de créer toutes les différentes surcharges Jsonp (), il suffit de convertir votre JsonResult en Jsonp.

13
mendicant

Article de blog de Ranj (alias "Cet article de blog que j'ai trouvé") est excellent, et sa lecture vous permettra d'approfondir la solution ci-dessous afin que votre contrôleur puisse gérer les requêtes JSON du même domaine et JSONP interdomaines élégamment dans la même action de contrôleur sans code supplémentaire [dans l'action].

Quoi qu'il en soit, pour les types "donnez-moi le code", le voici, au cas où le blog disparaîtrait à nouveau.

Dans votre contrôleur (cet extrait est un code nouveau/non-blog):

[AllowCrossSiteJson]
public ActionResult JsonpTime(string callback)
{
    string msg = DateTime.UtcNow.ToString("o");
    return new JsonpResult
    {
        Data = (new
        {
            time = msg
        })
    };
}

JsonpResult trouvé sur cet excellent article de blog :

/// <summary>
/// Renders result as JSON and also wraps the JSON in a call
/// to the callback function specified in "JsonpResult.Callback".
/// http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx
/// </summary>
public class JsonpResult : JsonResult
{
    /// <summary>
    /// Gets or sets the javascript callback function that is
    /// to be invoked in the resulting script output.
    /// </summary>
    /// <value>The callback function name.</value>
    public string Callback { get; set; }

    /// <summary>
    /// Enables processing of the result of an action method by a
    /// custom type that inherits from <see cref="T:System.Web.Mvc.ActionResult"/>.
    /// </summary>
    /// <param name="context">The context within which the
    /// result is executed.</param>
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        HttpResponseBase response = context.HttpContext.Response;
        if (!String.IsNullOrEmpty(ContentType))
            response.ContentType = ContentType;
        else
            response.ContentType = "application/javascript";

        if (ContentEncoding != null)
            response.ContentEncoding = ContentEncoding;

        if (Callback == null || Callback.Length == 0)
            Callback = context.HttpContext.Request.QueryString["callback"];

        if (Data != null)
        {
            // The JavaScriptSerializer type was marked as obsolete
            // prior to .NET Framework 3.5 SP1 
#pragma warning disable 0618
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string ser = serializer.Serialize(Data);
            response.Write(Callback + "(" + ser + ");");
#pragma warning restore 0618
        }
    }
}

Note: Suite à la commentaires à l'OP par @Ranju et autres , je me suis dit que ça valait la peine de poster le "nu code fonctionnel minimum du blog de Ranju en tant que wiki communautaire. Bien qu'il soit sûr de dire que Ranju a ajouté le code ci-dessus et d'autres sur son blog pour être utilisé librement, je ne vais pas copier ses mots ici.

10
ruffin

Les articles référencés de stimms et ranju v ont tous deux été très utiles et ont clarifié la situation.

Cependant, je me suis gratté la tête à propos de l'utilisation d'extensions, de la sous-classification dans le contexte du code MVC que j'avais trouvé en ligne.

Il y avait deux points clés qui m'ont rattrapé:

  1. Le code que j'avais dérivé d'ActionResult, mais dans ExecuteResult, il y avait du code pour renvoyer XML ou JSON.
  2. J'avais alors créé un ActionResult basé sur les génériques, pour m'assurer que les mêmes ExecuteResults étaient utilisés indépendamment du type de données que je retournais.

Donc, en combinant les deux - je n'avais pas besoin d'extensions ou de sous-classes supplémentaires pour ajouter le mécanisme de retour JSONP, changez simplement mes ExecuteResults existants.

Ce qui m'avait dérouté, c'est que je cherchais vraiment un moyen de dériver ou d'étendre JsonResult, sans recoder le ExecuteResult. Comme JSONP est effectivement une chaîne JSON avec préfixe et suffixe, cela semblait un gaspillage. Cependant, le sous-jacent ExecuteResult utilise respone.write - donc la façon la plus sûre de changer est de recoder ExecuteResults tel que fourni par diverses publications!

Je peux publier du code si cela peut être utile, mais il y a déjà beaucoup de code dans ce fil.

0
From Orbonia
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Web;
        using System.Web.Mvc;
        using System.Web.Script.Serialization;

        namespace Template.Web.Helpers
        {
            public class JsonpResult : JsonResult
            {
                public JsonpResult(string callbackName)
                {
                    CallbackName = callbackName;
                }

                public JsonpResult()
                    : this("jsoncallback")
                {
                }

                public string CallbackName { get; set; }

                public override void ExecuteResult(ControllerContext context)
                {
                    if (context == null)
                    {
                        throw new ArgumentNullException("context");
                    }

                    var request = context.HttpContext.Request;
                    var response = context.HttpContext.Response;

                    string jsoncallback = ((context.RouteData.Values[CallbackName] as string) ?? request[CallbackName]) ?? CallbackName;

                    if (!string.IsNullOrEmpty(jsoncallback))
                    {
                        if (string.IsNullOrEmpty(base.ContentType))
                        {
                            base.ContentType = "application/x-javascript";
                        }
                        response.Write(string.Format("{0}(", jsoncallback));
                    }

                    base.ExecuteResult(context);

                    if (!string.IsNullOrEmpty(jsoncallback))
                    {
                        response.Write(")");
                    }
                }
            }

            public static class ControllerExtensions
            {
                public static JsonpResult Jsonp(this Controller controller, object data, string callbackName = "callback")
                {
                    return new JsonpResult(callbackName)
                    {
                        Data = data,
                        JsonRequestBehavior = JsonRequestBehavior.AllowGet
                    };
                }

                public static T DeserializeObject<T>(this Controller controller, string key) where T : class
                {
                    var value = controller.HttpContext.Request.QueryString.Get(key);
                    if (string.IsNullOrEmpty(value))
                    {
                        return null;
                    }
                    JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
                    return javaScriptSerializer.Deserialize<T>(value);
                }
            }
        }

//Example of using the Jsonp function::
  //  1-
    public JsonResult Read()
            {
                IEnumerable<User> result = context.All();        

                return this.Jsonp(result);
            }
    //2-
    public JsonResult Update()
            {
                var models = this.DeserializeObject<IEnumerable<User>>("models");
                if (models != null)
                {
                    Update(models); //Update properties & save change in database
                }
                return this.Jsonp(models);
            }
0
K.Hicham