Использование JSON.NET в качестве стандартного JSON-сериализатора в ASP.NET MVC 3 - возможно ли это?
Можно ли использовать JSON.NET в качестве сериализатора JSON по умолчанию в ASP.NET MVC 3?
Согласно моим исследованиям, кажется, что единственный способ добиться этого - расширить ActionResult, поскольку JsonResult в MVC3 не является виртуальным...
Я надеялся, что с ASP.NET MVC 3 будет возможность указать подключаемый провайдер для сериализации в JSON.
Мысли?
Ответы
Ответ 1
Я считаю, что лучший способ сделать это - как описано в ваших ссылках - продлить действие ActionResult или расширить JsonResult напрямую.
Что касается метода JsonResult, который не является виртуальным на контроллере, который не является истинным, просто выберите правильную перегрузку. Это хорошо работает:
protected override JsonResult Json(object data, string contentType, Encoding contentEncoding)
РЕДАКТИРОВАТЬ 1: расширение JsonResult...
public class JsonNetResult : JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
var response = context.HttpContext.Response;
response.ContentType = !String.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
// If you need special handling, you can call another form of SerializeObject below
var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
response.Write(serializedObject);
}
РЕДАКТИРОВАТЬ 2: я удалил чек, чтобы данные были пустыми в соответствии с приведенными ниже рекомендациями. Это должно привести к тому, что новые версии JQuery будут счастливыми и, похоже, станут разумными, поскольку ответ можно безоговорочно десериализовать. Имейте в виду, однако, что это не поведение по умолчанию для ответов JSON от ASP.NET MVC, которое скорее отвечает пустой строкой, когда нет данных.
Ответ 2
Я реализовал это без использования базового контроллера или инъекции.
Я использовал фильтры действий, чтобы заменить JsonResult на JsonNetResult.
public class JsonHandlerAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonResult = filterContext.Result as JsonResult;
if (jsonResult != null)
{
filterContext.Result = new JsonNetResult
{
ContentEncoding = jsonResult.ContentEncoding,
ContentType = jsonResult.ContentType,
Data = jsonResult.Data,
JsonRequestBehavior = jsonResult.JsonRequestBehavior
};
}
base.OnActionExecuted(filterContext);
}
}
В приложении Global_asax.cs Application_Start() вам нужно добавить:
GlobalFilters.Filters.Add(new JsonHandlerAttribute());
Для полного удовлетворения, вот мой класс расширения JsonNetResult, который я выбрал из другого места, и что я немного изменил его, чтобы получить правильную поддержку пропаривания:
public class JsonNetResult : JsonResult
{
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Error
};
}
public JsonSerializerSettings Settings { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("JSON GET is not allowed");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;
if (this.ContentEncoding != null)
response.ContentEncoding = this.ContentEncoding;
if (this.Data == null)
return;
var scriptSerializer = JsonSerializer.Create(this.Settings);
scriptSerializer.Serialize(response.Output, this.Data);
}
}
Ответ 3
Используйте Newtonsoft JSON конвертер:
public ActionResult DoSomething()
{
dynamic cResponse = new ExpandoObject();
cResponse.Property1 = "value1";
cResponse.Property2 = "value2";
return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}
Ответ 4
Я знаю, что это хорошо после ответа на вопрос, но я использую другой подход, поскольку я использую инъекцию зависимостей для создания экземпляров моих контроллеров.
Я заменил IActionInvoker (путем инъекции свойства ControllerActionInvoker Property) с версией, которая переопределяет метод InvokeActionMethod.
Это означает отсутствие изменений в наследовании контроллера и его можно легко удалить при обновлении до MVC4 путем изменения регистрации контейнера DI для ВСЕХ контроллеров
public class JsonNetActionInvoker : ControllerActionInvoker
{
protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
ActionResult invokeActionMethod = base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
if ( invokeActionMethod.GetType() == typeof(JsonResult) )
{
return new JsonNetResult(invokeActionMethod as JsonResult);
}
return invokeActionMethod;
}
private class JsonNetResult : JsonResult
{
public JsonNetResult()
{
this.ContentType = "application/json";
}
public JsonNetResult( JsonResult existing )
{
this.ContentEncoding = existing.ContentEncoding;
this.ContentType = !string.IsNullOrWhiteSpace(existing.ContentType) ? existing.ContentType : "application/json";
this.Data = existing.Data;
this.JsonRequestBehavior = existing.JsonRequestBehavior;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet) && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
base.ExecuteResult(context); // Delegate back to allow the default exception to be thrown
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}
if (this.Data != null)
{
// Replace with your favourite serializer.
new Newtonsoft.Json.JsonSerializer().Serialize( response.Output, this.Data );
}
}
}
}
--- EDIT - Обновлено для отображения регистрации контейнера для контроллеров. Я использую Unity здесь.
private void RegisterAllControllers(List<Type> exportedTypes)
{
this.rootContainer.RegisterType<IActionInvoker, JsonNetActionInvoker>();
Func<Type, bool> isIController = typeof(IController).IsAssignableFrom;
Func<Type, bool> isIHttpController = typeof(IHttpController).IsAssignableFrom;
foreach (Type controllerType in exportedTypes.Where(isIController))
{
this.rootContainer.RegisterType(
typeof(IController),
controllerType,
controllerType.Name.Replace("Controller", string.Empty),
new InjectionProperty("ActionInvoker")
);
}
foreach (Type controllerType in exportedTypes.Where(isIHttpController))
{
this.rootContainer.RegisterType(typeof(IHttpController), controllerType, controllerType.Name);
}
}
public class UnityControllerFactory : System.Web.Mvc.IControllerFactory, System.Web.Http.Dispatcher.IHttpControllerActivator
{
readonly IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
}
IController System.Web.Mvc.IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return this.container.Resolve<IController>(controllerName);
}
SessionStateBehavior System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Required;
}
void System.Web.Mvc.IControllerFactory.ReleaseController(IController controller)
{
}
IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
return this.container.Resolve<IHttpController>(controllerType.Name);
}
}
Ответ 5
Развернув ответ от https://stackoverflow.com/users/183056/sami-beyoglu, если вы установите тип содержимого, тогда jQuery сможет преобразовать возвращенные данные в объект для вас.
public ActionResult DoSomething()
{
dynamic cResponse = new ExpandoObject();
cResponse.Property1 = "value1";
cResponse.Property2 = "value2";
return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}
Ответ 6
Я сделал версию, которая делает действия веб-сервиса безопасными и простыми. Вы используете его следующим образом:
public JsonResult<MyDataContract> MyAction()
{
return new MyDataContract();
}
Класс:
public class JsonResult<T> : JsonResult
{
public JsonResult(T data)
{
Data = data;
JsonRequestBehavior = JsonRequestBehavior.AllowGet;
}
public override void ExecuteResult(ControllerContext context)
{
// Use Json.Net rather than the default JavaScriptSerializer because it faster and better
if (context == null)
throw new ArgumentNullException("context");
var response = context.HttpContext.Response;
response.ContentType = !String.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
response.Write(serializedObject);
}
public static implicit operator JsonResult<T>(T d)
{
return new JsonResult<T>(d);
}
}
Ответ 7
Мой пост может помочь кому-то.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace MultipleSubmit.Service
{
public abstract class BaseController : Controller
{
protected override JsonResult Json(object data, string contentType,
Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new JsonNetResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}
}
}
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MultipleSubmit.Service
{
public class JsonNetResult : JsonResult
{
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Error
};
}
public JsonSerializerSettings Settings { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals
(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("JSON GET is not allowed");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(this.ContentType) ?
"application/json" : this.ContentType;
if (this.ContentEncoding != null)
response.ContentEncoding = this.ContentEncoding;
if (this.Data == null)
return;
var scriptSerializer = JsonSerializer.Create(this.Settings);
using (var sw = new StringWriter())
{
scriptSerializer.Serialize(sw, this.Data);
response.Write(sw.ToString());
}
}
}
}
public class MultipleSubmitController : BaseController
{
public JsonResult Index()
{
var data = obj1; // obj1 contains the Json data
return Json(data, JsonRequestBehavior.AllowGet);
}
}