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

Я хочу вернуть некоторые JSON через домены, и я понимаю, что способ сделать это через JSONP, а не чистый JSON. Я использую ASP.net MVC, поэтому я думал о просто расширении типа JSONResult, а затем расширителя, чтобы он также реализовал метод 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
            };
        }

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

Ответ 1

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

Клиентский код с помощью 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");
 }

Ответ 2

Вместо того, чтобы подклассифицировать мои контроллеры с помощью методов 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.

Ответ 3

Сообщение в блоге Ranju (aka "Это сообщение в блоге, которое я нашел" ) отлично, и его чтение позволит вам продолжить решение ниже так что ваш контроллер может обрабатывать 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
        }
    }
}

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

Ответ 4

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

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

Было два ключевых момента, которые застали меня:

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

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

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

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

Ответ 5

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

Ответ 6

приведенное выше решение является хорошим способом работы, но его следует расширять с помощью нового типа результата вместо того, чтобы иметь метод, который возвращает 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>");
    }
}