72 votes

ASP.net MVC retour JSONP

Je suis à la recherche d'un éventuel retour JSON dans les domaines et je comprends que la façon de le faire est par le biais de JSONP plutôt que la pure JSON. Je suis à l'aide de ASP.net MVC donc je pensais à une simple extension du JSONResult type et puis extendig Contrôleur afin qu'il soit également mis en place un Jsonp méthode. Est-ce la meilleure façon d'aller à ce sujet ou est-il intégré dans la ActionResult qui pourrait être mieux?

Edit: je suis allé de l'avant et fait. Juste pour la référence souci, 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 un couple de méthodes pour une super-classe de toutes 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.

17voto

Maksym Kozlenko Points 4557

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

Côté Client, le code à l'aide de jQuery:

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

MVC action du contrôleur. Retuerns contenu de résultat avec du code JavaScript de l'exécution de la fonction callback fournie avec la chaîne de requête. Aussi jeux de JavaScript type MIME de la réponse.

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

13voto

mendicant Points 489

Plutôt que de sous-classement de mes contrôleurs avec Jsonp (), je suis allé la méthode d'extension de l'itinéraire qu'il se sent une touche nettoyant pour moi. La bonne chose à propos de la JsonpResult est que vous pouvez tester c'est exactement de la même manière que vous le feriez pour 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 la création de tous les différents Jsonp() surcharges, il suffit de convertir votre JsonResult à un Jsonp.

10voto

ruffin Points 1906

Ranju du blog (aka "Ce blog, j'ai trouvé") est excellent, et sa lecture vous permettra de plus la solution ci-dessous afin que votre contrôleur peut gérer même domaine JSON et inter-domaine JSONP demandes élégamment dans la même action de contrôleur sans code supplémentaire [de l'action].

Peu importe, pour le "donnez-moi le code" types, c'est ici, dans le cas où le blog disparaît à nouveau.

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

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

JsonpResult trouvé sur cet excellent post 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
        }
    }
}

Remarque: Suite aux commentaires de l'OP par @Ranju et les autres, j'ai pensé qu'il valait la peine de poster le "strict minimum" code fonctionnel de Ranju du blog comme un wiki de la communauté. S'il est sûr de dire que Ranju ajouté ci-dessus et un autre code sur son blog pour être utilisé librement, je ne vais pas copier ses mots ici.

0voto

From Orbonia Points 103

Les articles référencés par stimms et ranju v ont été à la fois très utile et fait clairement la situation.

Cependant, j'ai été laissé à me gratter la tête à propos de l'utilisation des extensions, sous-classement dans le contexte de la MVC code que j'avais trouvé en ligne.

Il y a deux points clés qui m'a pris:

  1. Le code que j'avais dérivé de ActionResult, mais dans ExecuteResult il y avait un peu de code pour revenir au format XML ou JSON.
  2. J'avais alors créé un Génériques à base ActionResult, pour assurer le même ExecuteResults a été utilisé indépendamment du type de données que j'ai retourné.

Donc, en combinant les deux, je n'ai pas besoin de plus d'extensions ou de sous-classement d'ajouter le mécanisme de retour JSONP, il suffit de changer mon ExecuteResults.

Ce qui l'avait confondu moi, c'est que vraiment je cherchais un moyen de calculer ou de prolonger JsonResult, sans re-codage de l'ExecuteResult. Comme JSONP est effectivement une chaîne JSON avec préfixe et suffixe, il semble un gaspillage. Toutefois, le sous-fifre ExecuteResult utilise respone.écrivez - donc, le meilleur moyen de changer est de re-code ExecuteResults que la main fournis par diverses affectations!

Je peux poster du code, si c'est utile, mais il y a beaucoup de code dans ce fil déjà.

-2voto

mounir Points 11

la solution ci-dessus est une bonne façon de travailler, mais il devrait être extendend avec un nouveau type de résultat au lieu d'avoir une méthode qui retourne un JsonResult vous devriez écrire des méthodes qui renvoient vos propres types de résultats

public JsonPResult testMethod() {
    // use the other guys code to write a method that returns something
}

public class JsonPResult : JsonResult
{
    public FileUploadJsonResult(JsonResult data) {
        this.Data = data;
    }      

    public override void ExecuteResult(ControllerContext context)
    {
        this.ContentType = "text/html";
        context.HttpContext.Response.Write("<textarea>");
        base.ExecuteResult(context);
        context.HttpContext.Response.Write("</textarea>");
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X