ASP.net MVC возвращает JSONP - PullRequest
       36

ASP.net MVC возвращает JSONP

71 голосов
/ 17 апреля 2009

Я хочу вернуть JSON через домены, и я понимаю, что способ сделать это - через JSONP, а не через чистый JSON. Я использую ASP.net MVC, поэтому я думал просто о расширении типа JSONResult, а затем о расширении Controller, чтобы он также реализовал метод Jsonp. Это лучший способ или есть встроенный ActionResult, который может быть лучше?

Редактировать: я пошел вперед и сделал это. Просто для справки я добавил новый результат:

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

, а также пара методов для суперкласса всех моих контроллеров:

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

Работает как шарм.

Ответы [ 6 ]

16 голосов
/ 20 марта 2013

Вот простое решение, если вы не хотите определять фильтр действий

Код на стороне клиента с использованием jQuery:

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

Действие контроллера MVC. Возвращает результат содержимого с помощью кода JavaScript, выполняющего функцию обратного вызова, снабженную строкой запроса. Также устанавливает тип JavaScript MIME для ответа.

 public ContentResult JsonpCall(string callback)
 {
      return Content(String.Format("{0}({1});",
          callback, 
          new JavaScriptSerializer().Serialize(new { a = 1 })),    
          "application/javascript");
 }
13 голосов
/ 14 декабря 2010

Вместо того, чтобы создавать подклассы для моих контроллеров с помощью методов Jsonp (), я пошел по пути метода расширения, так как он кажется мне чище. Хорошая особенность JsonpResult заключается в том, что вы можете протестировать его точно так же, как и JsonResult.

Я сделал:

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

Таким образом, вам не нужно беспокоиться о создании всех различных перегрузок Jsonp (), просто конвертируйте ваш JsonResult в Jsonp.

10 голосов
/ 08 ноября 2013

Пост Ранджу в блоге (он же "Этот пост в блоге, который я нашел") превосходен, и его чтение позволит вам продвинуть решение ниже, чтобы ваш контроллер мог обрабатывать JSON и междоменный домен в одном домене JSONP элегантно запрашивает в одном действии контроллера без дополнительного кода [в действии].

Несмотря на это, для типов "дай мне код", это так, на случай, если блог снова исчезнет.

В вашем контроллере (этот фрагмент является новым / не блог-кодом):

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

JsonpResult найден на это отличное сообщение в блоге :

/// <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
        }
    }
}

Примечание: В ответ на комментарии к ОП @Ranju и других я решил, что стоит опубликовать функциональный код "минимума" из поста Ранджу в блоге как сообщество вики. Хотя можно с уверенностью сказать, что Ранджу добавил приведенный выше и другой код в свой блог для свободного использования, я не собираюсь копировать его слова здесь.

0 голосов
/ 27 августа 2015
        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 голосов
/ 16 июля 2011

Статьи по ссылкам на стимы и раню v были очень полезны и прояснили ситуацию.

Тем не менее, мне пришлось почесать голову об использовании расширений, подклассов в контексте кода MVC, который я нашел в Интернете.

Меня поймали два ключевых момента:

  1. Код, который я получил от ActionResult, но в ExecuteResult был некоторый код, возвращающий либо XML, либо JSON.
  2. Затем я создал ActionResult на основе Generics, чтобы гарантировать, что тот же ExecuteResults использовался независимо от типа возвращаемых данных.

Итак, объединяя два - мне не нужно было дополнительных расширений или подклассов для добавления механизма возврата JSONP, просто измените мои существующие ExecuteResults.

Что меня смутило, так это то, что я действительно искал способ получить или расширить JsonResult без перекодирования ExecuteResult. Поскольку JSONP, по сути, является строкой JSON с префиксом и суффиксом, это кажется пустой тратой. Однако подчиненный ExecuteResult использует respone.write - так что самый безопасный способ изменить это перекодировать ExecuteResults, как это удобно в различных публикациях!

Я могу опубликовать некоторый код, если это будет полезно, но в этой теме уже достаточно много кода.

0 голосов
/ 09 февраля 2011

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

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>");
    }
}
...