ASP.NET MVC 3 में JSON.NET डिफ़ॉल्ट JSON धारावाहिक के रूप में उपयोग करना - क्या यह संभव है?


101

क्या ASP.NET MVC 3 में JSON.NET को डिफ़ॉल्ट JSON धारावाहिक के रूप में उपयोग करना संभव है ?

मेरे शोध के अनुसार, ऐसा लगता है कि इसे पूरा करने का एकमात्र तरीका MVC3 में JsonResult के रूप में ActionResult का विस्तार करना है ...

मुझे आशा है कि ASP.NET MVC 3 के साथ कि JSON पर प्रसारित करने के लिए प्लग करने योग्य प्रदाता निर्दिष्ट करने का एक तरीका होगा।

विचार?


जवाबों:


106

मैं इसे करने का सबसे अच्छा तरीका मानता हूं, यह है - जैसा कि आपके लिंक में वर्णित है - 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);
    }

EDIT 2 : मैंने नीचे दिए सुझावों के अनुसार डेटा के लिए चेक को शून्य कर दिया है। यह JQuery के नए संस्करणों को खुश करना चाहिए और ऐसा लगता है कि ऐसा करने के लिए समझदारी की बात है, क्योंकि प्रतिक्रिया के बाद बिना शर्त डिस्क्राइबलाइज किया जा सकता है। हालाँकि, अवगत रहें, कि यह ASP.NET MVC के JSON प्रतिक्रियाओं के लिए डिफ़ॉल्ट व्यवहार नहीं है, जो कि एक खाली स्ट्रिंग के साथ प्रतिक्रिया करता है, जब कोई डेटा नहीं होता है।


1
कोड MySpecialContractResolver को संदर्भित करता है, जो परिभाषित नहीं है। यह प्रश्न उस के साथ मदद करता है (और उस समस्या से संबंधित था जिसे मुझे हल करना था): stackoverflow.com/questions/6700053/…
Elliveny

1
महान जवाब के लिए धन्यवाद। यदि क्यों (डेटा == अशक्त) वापसी; ? अपने उपयोग के मामले के लिए मैं JSON मानक जो भी था, जो Json.Net ईमानदारी से करता है, यहां तक ​​कि अशक्त ("null") को वापस करना चाहता था। शून्य मानों को इंटरसेप्ट करके आप इन के लिए खाली स्ट्रिंग भेजना समाप्त करते हैं, जो मानक से
विचलन

1
@ क्रिस मॉशचिनी: आप बिल्कुल सही कह रहे हैं। खाली स्ट्रिंग लौटना गलत है। लेकिन क्या इसे json value null या एक खाली json ऑब्जेक्ट वापस करना चाहिए? मुझे यकीन नहीं है कि एक मूल्य वापस आ रहा है जहां एक वस्तु अपेक्षित है या तो समस्या मुक्त है। लेकिन किसी भी तरह से, वर्तमान कोड इस संबंध में अच्छा नहीं है।
asgerhallas

1
Json.Net में एक बग है जो IE9 का कारण बनता है और आईएसओ 8601 डेट्स Json.Net को पार्स करने में विफल रहता है। इसके लिए फिक्स को इस उत्तर में शामिल किया गया है: stackoverflow.com/a/15939945/176877
क्रिस मोसचिनी

1
@asgerhallas, @ क्रिस मॉशचिनी डिफ़ॉल्ट asp.net mvc JsonResult चेक के बारे में क्या 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 का उपयोग करते हैं, तो क्या हमें उनकी आवश्यकता है?
क्रोमिगो

60

मैंने इसे आधार नियंत्रक या इंजेक्शन की आवश्यकता के बिना लागू किया।

मैंने 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 extention क्लास है जिसे मैंने कहीं और से उठाया था और जिसे मैंने सही स्टीमिंग सपोर्ट पाने के लिए थोड़ा संशोधित किया था:

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

1
यह एक अच्छा उपाय है। इसे बनाता है इसलिए मूल निवासी return Json()Json.Net का उपयोग करता है।
OneHoopyFrood

1
किसी के लिए भी आश्चर्य है कि यह कैसे काम करता है, यह इसे स्वीकार करता JsonResultहै Json()और इसे एक में परिवर्तित करता है JsonNetResult। यह ऐसा asकीवर्ड का उपयोग करता है जो रूपांतरण संभव नहीं होने पर अशक्त हो जाता है। बहुत निफ्टी। Gryffindor के लिए 10 अंक!
OneHoopyFrood

4
प्रश्न हालांकि, डिफ़ॉल्ट धारावाहिक को इंटरसेप्ट करने से पहले ऑब्जेक्ट पर चलता है?
OneHoopyFrood

यह एक शानदार जवाब है - सबसे लचीलेपन के साथ। चूंकि मेरी परियोजना पहले से ही सामने के छोर पर सभी प्रकार के मैनुअल समाधान कर रही थी, इसलिए मैं एक वैश्विक फ़िल्टर नहीं जोड़ सका - इसके लिए बड़े बदलाव की आवश्यकता होगी। मैंने केवल नियंत्रक क्रियाओं पर समस्या को हल करना समाप्त कर दिया है, जहां मेरे नियंत्रक के कार्यों पर विशेषता का उपयोग करके आवश्यक है। हालाँकि, मैंने इसे कॉल किया - [BetterJsonHandler]:-)।
सिम्हा खाबिंस्की

इसे वापस करना। Json (null); अभी भी कुछ भी नहीं है
ब्रुनिस

27

न्यूटनसॉफ्ट के JSON कनवर्टर का उपयोग करें:

public ActionResult DoSomething()
{
    dynamic cResponse = new ExpandoObject();
    cResponse.Property1 = "value1";
    cResponse.Property2 = "value2";
    return Content(JsonConvert.SerializeObject(cResponse), "application/json");
}

7
सुनिश्चित नहीं है कि यह हैकरी है या नहीं, लेकिन पवित्र बकवास विस्तार कक्षाओं को बनाने की तुलना में आसान है, बस एक बेवकूफ जौन स्ट्रिंग वापस करने के लिए।
डेनिस.शेपर्ड

21

मुझे पता है कि यह प्रश्न का उत्तर दिए जाने के बाद अच्छी तरह से है, लेकिन मैं एक अलग दृष्टिकोण का उपयोग कर रहा हूं क्योंकि मैं अपने नियंत्रकों को तुरंत करने के लिए निर्भरता इंजेक्शन का उपयोग कर रहा हूं।

मैंने IActionInvoker (नियंत्रक के नियंत्रकAvInvoker गुण को इंजेक्ट करके) को एक संस्करण के साथ बदल दिया है जो 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 );
            }
        }
    }
}

--- EDIT - नियंत्रकों के लिए कंटेनर पंजीकरण दिखाने के लिए अद्यतन किया गया। मैं यहां एकता का उपयोग कर रहा हूं।

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

अच्छा लगा, लेकिन आप इसका उपयोग कैसे करते हैं? या बेहतर है कि आपने इसे कैसे इंजेक्ट किया?
अडाप्टैबी

के .Serialize () स्ट्रीम फॉर्म का उपयोग करने के लिए +1। मैं इंगित करने जा रहा था कि आप अन्य शीर्ष उत्तर की तरह सिर्फ JsonConvert का उपयोग कर सकते हैं, लेकिन आपका दृष्टिकोण धीरे-धीरे लंबी / बड़ी वस्तुओं को प्रवाहित करता है - यह एक नि: शुल्क प्रदर्शन को बढ़ावा देता है, खासकर अगर डाउनस्ट्रीम क्लाइंट आंशिक प्रतिक्रियाओं को संभाल सकता है।
क्रिस मोशचिनी

1
अच्छा कार्यान्वयन। इसका उत्तर होना चाहिए!
कैट लिम रुइज

अच्छा जा रहा है, यह केवल एक चीज थी जिसके लिए मैं एक आधार नियंत्रक का उपयोग कर रहा था।
क्रिस डाइवर

वास्तव में अच्छा है - यह बहुत अच्छा है तो Json () फ़ंक्शन को ओवरराइड करना, क्योंकि हर स्थान पर जहां आप एक JsonResult लौटाएंगे, यह इसमें किक करेगा और यह जादू है। जो लोग DI का उपयोग नहीं करते हैं, उनके लिए बस अपने आधार नियंत्रक के लिए सुरक्षित ओवरराइड IActionInvoker CreateActionInvoker () {नया JsonNetActionInvoker ();} वापस जोड़ें
Avi Pinto

13

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

धन्यवाद, मेरे पास एक संकर मिश्रण है और यह केवल एक चीज है जो मेरे लिए काम करेगी।
किया_मर्सन

मैंने इसे JSON.NET के साथ इस तरह इस्तेमाल किया: JObject jo = GetJSON(); return Content(jo.ToString(), "application/json");
John Mott

6

मेरी पोस्ट किसी की मदद कर सकती है।

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
एंड्रयूज

4

मैंने एक संस्करण बनाया है जो वेब सेवा कार्यों को सुरक्षित और सरल बनाता है। आप इसे इस तरह उपयोग करते हैं:

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

लेकिन तुम क्यों एक जोरदार प्रकार JsonResult है चाहता हूँ? : आप अनाम प्रकार के परिणामों को ढीला कर देते हैं और ग्राहक की ओर से कुछ भी नहीं कमाते हैं, क्योंकि इसके सी # क्लासेज का उपयोग नहीं किया जाता है?
मिकस

1
@mikus यह सर्वर साइड पर टाइप करने योग्य है: विधि को MyDataContract टाइप करना होगा। यह ग्राहक पक्ष को स्पष्ट करता है कि डेटा संरचना क्या लौटा रही है। यह संक्षिप्त और पठनीय भी है - JsonResult <T> किसी भी प्रकार से Json में लौटाया जा रहा है और आपको कुछ भी करने की आवश्यकता नहीं है।
कर्टिस यलोप
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.