ASP.NET वेब एपीआई के साथ JSONP


136

मैं वेब API का उपयोग करके ASP.MVC MVC 4 में सेवाओं का एक नया सेट बनाने पर काम कर रहा हूं। अब तक, यह बहुत अच्छा है। मैंने सेवा बनाई है और इसे काम करने के लिए दिया है, और अब मैं JQuery का उपयोग करके इसका उपभोग करने की कोशिश कर रहा हूं। मैं FSONler का उपयोग करके JSON स्ट्रिंग वापस प्राप्त कर सकता हूं, और यह ठीक प्रतीत होता है, लेकिन क्योंकि यह सेवा एक अलग साइट पर मौजूद है, इसलिए इसे "नहीं अनुमति" के साथ JQuery त्रुटियों के साथ कॉल करने की कोशिश कर रहा है। तो, यह स्पष्ट रूप से एक मामला है जहां मुझे JSONP का उपयोग करने की आवश्यकता है।

मुझे पता है कि वेब एपीआई नया है, लेकिन मुझे उम्मीद है कि कोई मेरी मदद कर सकता है।

मैं JSONP का उपयोग करके वेब API विधि पर कॉल कैसे कर सकता हूं?


1
सिर्फ Channel9 पर स्कॉटगू वीडियो देखने के बाद, और स्कॉट हैन्समैन लेख को पढ़ने के बाद नए वेब एपीआई ढांचे को देख रहा था, और यह इस पर मेरे पहले विचारों / प्रश्नों में से एक था।
ट्रैकर 1

जवाबों:


132

इस सवाल को पूछने के बाद, मुझे अंततः वही मिला जिसकी मुझे आवश्यकता थी, इसलिए मैं इसका उत्तर दे रहा हूं।

मैं इस JsonpMediaTypeFormatter में भाग गया । इसे करके Application_Startअपने Global.asax में जोड़ें :

var config = GlobalConfiguration.Configuration;
config.Formatters.Insert(0, new JsonpMediaTypeFormatter());

और आप एक JQuery AJAX कॉल के साथ जाना अच्छा है जो इस तरह दिखता है:

$.ajax({
    url: 'http://myurl.com',
    type: 'GET',
    dataType: 'jsonp',
    success: function (data) {
        alert(data.MyProperty);
    }
})

यह बहुत अच्छी तरह से काम करने लगता है।


मेरे मामले में काम नहीं कर रहा है, जहां मेरे पास पहले से ही Json.Net सीरियललाइज़ेशन के लिए एक फॉर्मेटर जोड़ा गया है। कोई विचार?
जस्टिन

4
मेरा मानना ​​है कि Matter4ontext को MVC4 RC संस्करण मंचों
Kumar

13
कोड अब NuGet में WebApiContrib का हिस्सा है। मैन्युअल रूप से इसे खींचने की आवश्यकता नहीं है।
जॉन ऑनस्टॉट

7
हां, अब बस: "इंस्टॉल-पैकेज WebApiContrib.Formatting.Jsonp" डोको
nootot

4
यह वही है जो मुझे आज के nuget डाउनलोड का उपयोग करना था:GlobalConfiguration.Configuration.AddJsonpFormatter(config.Formatters.JsonFormatter, "callback");
joym8

52

यहाँ WebAPI RC के साथ उपयोग के लिए JsonpMediaTypeFormatter का अद्यतन संस्करण है:

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
    private string callbackQueryParameter;

    public JsonpMediaTypeFormatter()
    {
        SupportedMediaTypes.Add(DefaultMediaType);
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

        MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", DefaultMediaType));
    }

    public string CallbackQueryParameter
    {
        get { return callbackQueryParameter ?? "callback"; }
        set { callbackQueryParameter = value; }
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
    {
        string callback;

        if (IsJsonpRequest(out callback))
        {
            return Task.Factory.StartNew(() =>
            {
                var writer = new StreamWriter(stream);
                writer.Write(callback + "(");
                writer.Flush();

                base.WriteToStreamAsync(type, value, stream, content, transportContext).Wait();

                writer.Write(")");
                writer.Flush();
            });
        }
        else
        {
            return base.WriteToStreamAsync(type, value, stream, content, transportContext);
        }
    }


    private bool IsJsonpRequest(out string callback)
    {
        callback = null;

        if (HttpContext.Current.Request.HttpMethod != "GET")
            return false;

        callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

        return !string.IsNullOrEmpty(callback);
    }
}

8
बहुत बढ़िया धन्यवाद, हालांकि मेरा मानना ​​है कि WriteToStreamAsync को अंतिम रिलीज में अब HttpContentHeaders ऑब्जेक्ट नहीं एक HttpContent लेना चाहिए, लेकिन इसके साथ ही एक बदलाव ने एक आकर्षण की तरह काम किया
Ben

21

आप इस तरह ActionFilterAttribute का उपयोग कर सकते हैं:

public class JsonCallbackAttribute : ActionFilterAttribute
{
    private const string CallbackQueryParameter = "callback";

    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var callback = string.Empty;

        if (IsJsonp(out callback))
        {
            var jsonBuilder = new StringBuilder(callback);

            jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

            context.Response.Content = new StringContent(jsonBuilder.ToString());
        }

        base.OnActionExecuted(context);
    }

    private bool IsJsonp(out string callback)
    {
        callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

        return !string.IsNullOrEmpty(callback);
    }
}

फिर इसे अपनी कार्रवाई पर रखें:

[JsonCallback]
public IEnumerable<User> User()
{
    return _user;
}

VS2013 U5, MVC5.2 और WebApi 2 के साथ पूरी तरह से काम किया
यारला

11

निश्चित रूप से ब्रायन का उत्तर सही है, हालांकि यदि आप पहले से ही Json.Net फॉर्मैटर का उपयोग कर रहे हैं, जो आपको बहुत ही जूसन डेट्स और तेज़ी से सीरियलाइज़ेशन देता है, तो आप सिर्फ jsonp के लिए दूसरा फॉर्मेटर नहीं जोड़ सकते, आपको दोनों को मिलाना होगा। वैसे भी इसका उपयोग करना एक अच्छा विचार है, क्योंकि स्कॉट हैंसेलमैन ने कहा है कि ASP.NET वेब एपीआई की रिलीज़ डिफ़ॉल्ट रूप से Json.Net धारावाहिक का उपयोग करने वाली है।

public class JsonNetFormatter : MediaTypeFormatter
    {
        private JsonSerializerSettings _jsonSerializerSettings;
        private string callbackQueryParameter;

        public JsonNetFormatter(JsonSerializerSettings jsonSerializerSettings)
        {
            _jsonSerializerSettings = jsonSerializerSettings ?? new JsonSerializerSettings();

            // Fill out the mediatype and encoding we support
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
            Encoding = new UTF8Encoding(false, true);

            //we also support jsonp.
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
            MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", "application/json"));
        }

        public string CallbackQueryParameter
        {
            get { return callbackQueryParameter ?? "jsoncallback"; }
            set { callbackQueryParameter = value; }
        }

        protected override bool CanReadType(Type type)
        {
            if (type == typeof(IKeyValueModel))
                return false;

            return true;
        }

        protected override bool CanWriteType(Type type)
        {
            return true;
        }

        protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders,
            FormatterContext formatterContext)
        {
            // Create a serializer
            JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);

            // Create task reading the content
            return Task.Factory.StartNew(() =>
            {
                using (StreamReader streamReader = new StreamReader(stream, Encoding))
                {
                    using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
                    {
                        return serializer.Deserialize(jsonTextReader, type);
                    }
                }
            });
        }

        protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders,
            FormatterContext formatterContext, TransportContext transportContext)
        {
            string callback;
            var isJsonp = IsJsonpRequest(formatterContext.Response.RequestMessage, out callback);

            // Create a serializer
            JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);

            // Create task writing the serialized content
            return Task.Factory.StartNew(() =>
            {
                using (JsonTextWriter jsonTextWriter = new JsonTextWriter(new StreamWriter(stream, Encoding)) { CloseOutput = false })
                {
                    if (isJsonp)
                    {
                        jsonTextWriter.WriteRaw(callback + "(");
                        jsonTextWriter.Flush();
                    }

                    serializer.Serialize(jsonTextWriter, value);
                    jsonTextWriter.Flush();

                    if (isJsonp)
                    {
                        jsonTextWriter.WriteRaw(")");
                        jsonTextWriter.Flush();
                    }
                }
            });
        }

        private bool IsJsonpRequest(HttpRequestMessage request, out string callback)
        {
            callback = null;

            if (request.Method != HttpMethod.Get)
                return false;

            var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
            callback = query[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
    }

हम एएसपी .NET वेब एपीआई आरसी के लिए यह कैसे कर सकते हैं?
जोंपरल

आरसी संस्करण में भी रुचि
थॉमस स्टॉक


6

JSONP केवल Http GET अनुरोध के साथ काम करता है। Asp.net web api में एक CORS सपोर्ट है जो सभी http वर्ब्स के साथ अच्छा काम करता है।

यह लेख आपके लिए मददगार हो सकता है।


1
अब Web API में CORS सपोर्ट है। यह लेख बहुत उपयोगी है - asp.net/web-api/overview/security/…
Ilia Barahovski

5

अपडेट किया गया

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
    {
        private string callbackQueryParameter;

        public JsonpMediaTypeFormatter()
        {
            SupportedMediaTypes.Add(DefaultMediaType);
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

            MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", DefaultMediaType));
        }

        public string CallbackQueryParameter
        {
            get { return callbackQueryParameter ?? "callback"; }
            set { callbackQueryParameter = value; }
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            string callback;

            if (IsJsonpRequest(out callback))
            {
                return Task.Factory.StartNew(() =>
                {
                    var writer = new StreamWriter(writeStream);
                    writer.Write(callback + "(");
                    writer.Flush();

                    base.WriteToStreamAsync(type, value, writeStream, content, transportContext).Wait();

                    writer.Write(")");
                    writer.Flush();
                });
            }
            else
            {
                return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
            }
        }

        private bool IsJsonpRequest(out string callback)
        {
            callback = null;

            if (HttpContext.Current.Request.HttpMethod != "GET")
                return false;

            callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
    }

धन्यवाद, अन्य संस्करण नवीनतम .net ढांचे में काम नहीं करता है।
djbielejeski

2

यहां कई सुधारों के साथ एक अद्यतन संस्करण है, जो वेब एपीआई के RTM संस्करण के साथ काम करता है।

  • अनुरोध के स्वयं के Accept-Encodingहेडर के आधार पर, सही एन्कोडिंग का चयन करता है । new StreamWriter()पिछले उदाहरणों में बस UTF-8 का प्रयोग करेंगे। कॉल base.WriteToStreamAsyncएक अलग एन्कोडिंग का उपयोग कर सकता है, जिसके परिणामस्वरूप दूषित आउटपुट होता है।
  • application/javascript Content-Typeहेडर के लिए मैप्स JSONP अनुरोध ; पिछला उदाहरण JSONP आउटपुट करेगा, लेकिन application/jsonहेडर के साथ । यह काम नेस्टेड Mappingक्लास में किया जाता है (cf. JSONP की सेवा के लिए सबसे अच्छी सामग्री प्रकार? )
  • हमेशा के लिए निर्माण और फ्लशिंग ओवरहेड को StreamWriterसीधा करता है और सीधे बाइट्स प्राप्त करता है और उन्हें आउटपुट स्ट्रीम पर लिखता है।
  • किसी कार्य पर प्रतीक्षा करने के बजाय, ContinueWithकई कार्यों को एक साथ करने के लिए टास्क पैरेलल लाइब्रेरी के तंत्र का उपयोग करें ।

कोड:

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
  private string _callbackQueryParameter;

  public JsonpMediaTypeFormatter()
  {
    SupportedMediaTypes.Add(DefaultMediaType);
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/javascript"));

    // need a lambda here so that it'll always get the 'live' value of CallbackQueryParameter.
    MediaTypeMappings.Add(new Mapping(() => CallbackQueryParameter, "application/javascript"));
  }

  public string CallbackQueryParameter
  {
    get { return _callbackQueryParameter ?? "callback"; }
    set { _callbackQueryParameter = value; }
  }

  public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
                                          TransportContext transportContext)
  {
    var callback = GetCallbackName();

    if (!String.IsNullOrEmpty(callback))
    {
      // select the correct encoding to use.
      Encoding encoding = SelectCharacterEncoding(content.Headers);

      // write the callback and opening paren.
      return Task.Factory.StartNew(() =>
        {
          var bytes = encoding.GetBytes(callback + "(");
          writeStream.Write(bytes, 0, bytes.Length);
        })
      // then we do the actual JSON serialization...
      .ContinueWith(t => base.WriteToStreamAsync(type, value, writeStream, content, transportContext))

      // finally, we close the parens.
      .ContinueWith(t =>
        {
          var bytes = encoding.GetBytes(")");
          writeStream.Write(bytes, 0, bytes.Length);
        });
    }
    return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
  }

  private string GetCallbackName()
  {
    if (HttpContext.Current.Request.HttpMethod != "GET")
      return null;
    return HttpContext.Current.Request.QueryString[CallbackQueryParameter];
  }

  #region Nested type: Mapping

  private class Mapping : MediaTypeMapping
  {
    private readonly Func<string> _param; 

    public Mapping(Func<string> discriminator, string mediaType)
      : base(mediaType)
    {
      _param = discriminator;
    }

    public override double TryMatchMediaType(HttpRequestMessage request)
    {
      if (request.RequestUri.Query.Contains(_param() + "="))
        return 1.0;
      return 0.0;
    }
  }

  #endregion
}

मुझे Func<string>इनर क्लास कंस्ट्रक्टर में पैरामीटर की "हैकनेस" के बारे में पता है , लेकिन यह समस्या को हल करने का सबसे तेज़ तरीका था - जो सी # के बाद से ही स्टैटिक इनर क्लासेस है, यह CallbackQueryParameterप्रॉपर्टी नहीं देख सकता । पासिंग Funcलैम्ब्डा में संपत्ति बांध में है, इसलिए Mappingमें पर बाद में इसे उपयोग करने में सक्षम हो जाएगा TryMatchMediaType। यदि आपके पास अधिक सुरुचिपूर्ण तरीका है, तो कृपया टिप्पणी करें!


2

दुर्भाग्य से, मेरे पास टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा नहीं है, इसलिए मैं एक उत्तर पोस्ट करूंगा। @Justin चलाने का मुद्दा उठाया WebApiContrib.Formatting.Jsonp मानक JsonFormatter साथ फ़ॉर्मेटर। उस समस्या को नवीनतम रिलीज़ (वास्तव में कुछ समय पहले जारी किया गया) में हल किया गया है। इसके अलावा, यह नवीनतम वेब एपीआई रिलीज के साथ काम करना चाहिए।


1

जॉपरल, थॉमस। ऊपर पीटर पीटर रॉबर्ट द्वारा दिया गया उत्तर JsonMediaTypeFormatter के रूप में RC संस्करण के लिए सही होना चाहिए, जो कि उन्हें पहले से ही न्यूटनॉफ्ट जोंस धारावाहिक के उपयोग से विरासत में मिला है, और इसलिए उन्हें किसी भी बदलाव के साथ काम करना चाहिए।

हालांकि, पृथ्वी पर लोग अभी भी मापदंडों का उपयोग क्यों कर रहे हैं, जब आप बस निम्नलिखित कर सकते हैं

public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
        {
            var isJsonpRequest = IsJsonpRequest();

            if(isJsonpRequest.Item1)
            {
                return Task.Factory.StartNew(() =>
                {
                    var writer = new StreamWriter(stream);
                    writer.Write(isJsonpRequest.Item2 + "(");
                    writer.Flush();
                    base.WriteToStreamAsync(type, value, stream, contentHeaders, transportContext).Wait();
                    writer.Write(")");
                    writer.Flush();
                });
            }

            return base.WriteToStreamAsync(type, value, stream, contentHeaders, transportContext);
        }

        private Tuple<bool, string> IsJsonpRequest()
        {
            if(HttpContext.Current.Request.HttpMethod != "GET")
                return new Tuple<bool, string>(false, null);

            var callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return new Tuple<bool, string>(!string.IsNullOrEmpty(callback), callback);
        }

1

अपने स्वयं के JSONP फॉर्मैटर संस्करण की मेजबानी करने के बजाय आप WebApiContrib.Formatting.Jsonp स्थापित कर सकते हैं पहले से लागू एक के साथ NuGet पैकेज (वह संस्करण चुनें जो आपके .NET फ्रेमवर्क के लिए काम करता है)।

इस फ़ॉर्मेटर को इसमें जोड़ें Application_Start:

GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter(new JsonMediaTypeFormatter()));

0

आपमें से जो HttpSelfHostServer का उपयोग कर रहे हैं, उनके लिए कोड का यह खंड HttpContext.Current पर विफल होगा, क्योंकि यह स्वयं होस्ट सर्वर पर मौजूद नहीं है।

private Tuple<bool, string> IsJsonpRequest()
{
if(HttpContext.Current.Request.HttpMethod != "GET")
 return new Tuple<bool, string>(false, null);
 var callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];
 return new Tuple<bool, string>(!string.IsNullOrEmpty(callback), callback);
 }

हालाँकि आप इस ओवरराइड के माध्यम से स्वयं होस्ट "संदर्भ" को रोक सकते हैं।

public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
        {
            _method = request.Method;
            _callbackMethodName =
                request.GetQueryNameValuePairs()
                       .Where(x => x.Key == CallbackQueryParameter)
                       .Select(x => x.Value)
                       .FirstOrDefault();

            return base.GetPerRequestFormatterInstance(type, request, mediaType);
        }

Request.Method आपको "GET", "POST" आदि देगा और GetQueryNameValuePairs कॉलबैक पैरामीटर को पुनः प्राप्त कर सकता है। इस प्रकार मेरा संशोधित कोड इस तरह दिखता है:

private Tuple<bool, string> IsJsonpRequest()
 {
     if (_method.Method != "GET")
     return new Tuple<bool, string>(false, null);

     return new Tuple<bool, string>(!string.IsNullOrEmpty(_callbackMethodName), _callbackMethodName);
}

उम्मीद है कि यह आप में से कुछ की मदद करता है। इस तरह से आपको जरूरी नहीं कि HttpContext शिम चाहिए।

सी।



0

यदि संदर्भ है Web Api, धन्यवाद और 010227leoउत्तर देने के लिए, आपको उस WebContext.Currentमूल्य पर विचार करना चाहिए जो होने जा रहा है null

इसलिए मैंने उसका कोड इस पर अपडेट किया:

public class JsonCallbackAttribute
    : ActionFilterAttribute
{
    private const string CallbackQueryParameter = "callback";

    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var callback = context.Request.GetQueryNameValuePairs().Where(item => item.Key == CallbackQueryParameter).Select(item => item.Value).SingleOrDefault();

        if (!string.IsNullOrEmpty(callback))
        {
            var jsonBuilder = new StringBuilder(callback);

            jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

            context.Response.Content = new StringContent(jsonBuilder.ToString());
        }

        base.OnActionExecuted(context);
    }
}

0

हम दो तरीकों का उपयोग करके कॉर्स (क्रॉस-ऑरिजनल रिसोर्स शेयरिंग) मुद्दे को हल कर सकते हैं,

1) Jsonp 2 का उपयोग करना) Cors को सक्षम करना

1) Jsonp का उपयोग करते हुए- Jsonp का उपयोग करने के लिए हमें WebApiContrib.Formatting.Jsonp नगेट पैकेज स्थापित करने की आवश्यकता है और WebApiConfig.cs स्क्रीनशॉट में JsonpFormmater जोड़ने की आवश्यकता है,यहां छवि विवरण दर्ज करें

जक्वरी कोड यहां छवि विवरण दर्ज करें

2) कोर्स को सक्षम करना -

Cors को सक्षम करने के लिए हमें Microsoft.AspNet.WebApi.Cors nuget पैकेज जोड़ने की आवश्यकता है और WebApiConfig.cs संदर्भ स्क्रीनशॉट में cors सक्षम करने की आवश्यकता है

यहां छवि विवरण दर्ज करें

अधिक संदर्भ के लिए, आप निम्न लिंक का उपयोग करके GitHub पर मेरे नमूना रेपो का उल्लेख कर सकते हैं। https://github.com/mahesh353/Ninject.WebAPi/tree/develop

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.