ASP.NET MVC 3에서 JSON.NET 을 기본 JSON 직렬 변환기로 사용할 수 있습니까?
내 연구에 따르면이를 수행하는 유일한 방법 은 MVC3의 JsonResult가 가상이 아니기 때문에 ActionResult 를 확장 하는 것 같습니다 .
ASP.NET MVC 3에서 JSON으로 직렬화하기위한 플러그 형 공급자를 지정하는 방법이 있기를 바랐습니다.
생각?
ASP.NET MVC 3에서 JSON.NET 을 기본 JSON 직렬 변환기로 사용할 수 있습니까?
내 연구에 따르면이를 수행하는 유일한 방법 은 MVC3의 JsonResult가 가상이 아니기 때문에 ActionResult 를 확장 하는 것 같습니다 .
ASP.NET MVC 3에서 JSON으로 직렬화하기위한 플러그 형 공급자를 지정하는 방법이 있기를 바랐습니다.
생각?
답변:
가장 좋은 방법은 링크에 설명 된대로 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 : 아래 제안에 따라 데이터가 null인지 확인을 제거했습니다. 그러면 최신 버전의 JQuery가 만족스럽고 응답이 무조건 역 직렬화 될 수 있으므로 정상적인 작업처럼 보입니다. 그러나 이는 데이터가 없을 때 빈 문자열로 응답하는 ASP.NET MVC의 JSON 응답에 대한 기본 동작이 아닙니다.
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
어떻습니까? 이 검사를 응답에 추가해야한다고 생각합니다 (내부 MvcResources.JsonRequest_GetNotAllowed
메시지 없이 일부 사용자 지정 메시지 포함). 또한 2 개의 다른 기본 asp.net mvc 검사 (MaxJsonLength 및 RecursionLimit)는 어떻습니까? json.net을 사용하는 경우 필요합니까?
기본 컨트롤러 나 주입없이 이것을 구현했습니다.
액션 필터를 사용하여 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);
}
}
return Json()
가 실제로 Json.Net을 사용 하도록 만듭니다 .
JsonResult
에서 Json()
A와 개종자를 JsonNetResult
. as
변환이 불가능한 경우 null을 반환 하는 키워드를 사용 합니다. 아주 멋져요. 그리핀도르에게 10 점!
[BetterJsonHandler]
:-) 라고 불렀다 .
Newtonsoft의 JSON 변환기 사용 :
public ActionResult DoSomething()
{
dynamic cResponse = new ExpandoObject();
cResponse.Property1 = "value1";
cResponse.Property2 = "value2";
return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}
질문에 대한 답변을받은 후에도 이것이 잘된다는 것을 알고 있지만 종속성 주입을 사용하여 컨트롤러를 인스턴스화하기 때문에 다른 접근 방식을 사용하고 있습니다.
IActionInvoker (컨트롤러의 ControllerActionInvoker Property 주입)를 InvokeActionMethod 메서드를 재정의하는 버전으로 대체했습니다.
즉, 컨트롤러 상속에 대한 변경이 없으며 모든 컨트롤러에 대한 DI 컨테이너의 등록을 변경하여 MVC4로 업그레이드 할 때 쉽게 제거 할 수 있습니다.
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 );
}
}
}
}
--- 편집-컨트롤러에 대한 컨테이너 등록을 표시하도록 업데이트되었습니다. 여기서 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);
}
}
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");
}
JObject jo = GetJSON(); return Content(jo.ToString(), "application/json");
내 게시물은 누군가를 도울 수 있습니다.
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);
}
}
BaseController
은 클래스를 추가하고 업데이트하는 것만으로도 가장 적은 영향을 미쳤습니다 BaseController
.
웹 서비스 작업을 유형 안전하고 간단하게 만드는 버전을 만들었습니다. 다음과 같이 사용합니다.
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's 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);
}
}