Saya sedang mengerjakan membuat satu set layanan baru di ASP.MVC MVC 4 menggunakan API Web. Sejauh ini, itu bagus. Saya telah membuat layanan dan membuatnya berfungsi, dan sekarang saya mencoba mengonsumsinya menggunakan JQuery. Saya bisa mendapatkan kembali string JSON menggunakan Fiddler, dan tampaknya baik-baik saja, tetapi karena layanan tersebut ada di situs terpisah, mencoba memanggilnya dengan kesalahan JQuery dengan "Tidak Diizinkan". Jadi, ini jelas kasus di mana saya perlu menggunakan JSONP.
Saya tahu bahwa API Web masih baru, tetapi saya berharap seseorang di luar sana dapat membantu saya.
Bagaimana cara melakukan panggilan ke metode API Web menggunakan JSONP?
jquery
jsonp
asp.net-mvc-4
asp.net-web-api
Brian McCord
sumber
sumber
Jawaban:
Setelah menanyakan pertanyaan ini, saya akhirnya menemukan apa yang saya butuhkan, jadi saya menjawabnya.
Saya menemukan JsonpMediaTypeFormatter ini . Tambahkan ke dalam
Application_Start
global.asax Anda dengan melakukan ini:var config = GlobalConfiguration.Configuration; config.Formatters.Insert(0, new JsonpMediaTypeFormatter());
dan Anda siap menggunakan panggilan JQuery AJAX yang terlihat seperti ini:
$.ajax({ url: 'http://myurl.com', type: 'GET', dataType: 'jsonp', success: function (data) { alert(data.MyProperty); } })
Tampaknya bekerja dengan sangat baik.
sumber
GlobalConfiguration.Configuration.AddJsonpFormatter(config.Formatters.JsonFormatter, "callback");
Berikut adalah versi terbaru dari JsonpMediaTypeFormatter untuk digunakan dengan WebAPI RC:
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); } }
sumber
Anda dapat menggunakan ActionFilterAttribute seperti ini:
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); } }
Kemudian taruh di tindakan Anda:
[JsonCallback] public IEnumerable<User> User() { return _user; }
sumber
Tentu jawaban Brian benar, namun jika Anda sudah menggunakan formatter Json.Net, yang memberi Anda tanggal json cantik dan serialisasi lebih cepat, maka Anda tidak bisa menambahkan formatter kedua untuk jsonp, Anda harus menggabungkan keduanya. Itu adalah ide yang baik untuk tetap menggunakannya, seperti yang dikatakan Scott Hanselman bahwa rilis dari ASP.NET Web API akan menggunakan serializer Json.Net secara default.
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); } }
sumber
Penerapan Rick Strahl bekerja paling baik untuk saya dengan RC.
sumber
JSONP hanya bekerja dengan permintaan Http GET. Ada dukungan CORS di api web asp.net yang bekerja dengan baik dengan semua verba http.
Artikel ini mungkin berguna untuk Anda.
sumber
Diperbarui
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); } }
sumber
Berikut adalah versi yang diperbarui dengan beberapa peningkatan, yang berfungsi dengan versi RTM Web API.
Accept-Encoding
header permintaan itu sendiri . Padanew StreamWriter()
contoh sebelumnya hanya akan menggunakan UTF-8. Panggilan kebase.WriteToStreamAsync
mungkin menggunakan pengkodean yang berbeda, yang mengakibatkan keluaran rusak.application/javascript
Content-Type
header; contoh sebelumnya akan mengeluarkan JSONP, tetapi denganapplication/json
header. Pekerjaan ini dilakukan diMapping
kelas bersarang (cf. Tipe konten terbaik untuk melayani JSONP? )StreamWriter
dan secara langsung mendapatkan byte dan menulisnya ke aliran keluaran.ContinueWith
mekanisme Task Parallel Library untuk merangkai beberapa tugas bersama.Kode:
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 }
Saya menyadari
Func<string>
parameter "hackiness" dalam konstruktor kelas dalam, tetapi itu adalah cara tercepat untuk mengatasi masalah yang dipecahkannya - karena C # hanya memiliki kelas dalam statis, ia tidak dapat melihatCallbackQueryParameter
properti. MeneruskanFunc
masuk mengikat properti di lambda, sehinggaMapping
akan dapat mengaksesnya nanti diTryMatchMediaType
. Jika Anda memiliki cara yang lebih elegan, silakan berkomentar!sumber
Sayangnya, saya tidak memiliki reputasi yang cukup untuk berkomentar, jadi saya akan memposting jawaban. @Justin mengangkat masalah menjalankan pemformat WebApiContrib.Formatting.Jsonp bersama dengan JsonFormatter standar. Masalah itu diatasi dalam rilis terbaru (sebenarnya dirilis beberapa waktu lalu). Selain itu, ini harus bekerja dengan rilis API Web terbaru.
sumber
johperl, Thomas. Jawaban yang diberikan oleh Peter Moberg di atas harus benar untuk versi RC karena JsonMediaTypeFormatter yang dia warisi dari menggunakan serializer NewtonSoft Json, dan apa yang dia miliki harus bekerja tanpa perubahan apapun.
Namun, mengapa orang masih menggunakan parameter, padahal Anda bisa melakukan hal berikut
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); }
sumber
Alih-alih menghosting versi pemformat JSONP Anda sendiri, Anda dapat menginstal paket WebApiContrib.Formatting.Jsonp NuGet dengan paket yang sudah diimplementasikan (pilih versi yang sesuai untuk .NET Framework Anda).
Tambahkan formatter ini ke
Application_Start
:GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter(new JsonMediaTypeFormatter()));
sumber
Bagi Anda yang menggunakan HttpSelfHostServer, bagian kode ini akan gagal di HttpContext.Current, karena tidak ada di server host mandiri.
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); }
Namun Anda dapat menghentikan "konteks" host sendiri melalui penggantian ini.
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 akan memberi Anda "GET", "POST", dll. Dan GetQueryNameValuePairs dapat mengambil parameter? Callback. Jadi kode revisi saya terlihat seperti:
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); }
Semoga ini bisa membantu sebagian dari Anda. Dengan cara ini Anda tidak perlu shim HttpContext.
C.
sumber
Lihat yang ini. Lihat apakah itu membantu.
JSONP dengan Web API
sumber
Jika konteksnya adalah
Web Api
, berterima kasih dan mengacu pada010227leo
jawaban, Anda harus mempertimbangkanWebContext.Current
nilai yang akan menjadinull
.Jadi saya memperbarui kodenya menjadi ini:
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); } }
sumber
Kami dapat menyelesaikan masalah CORS (Cross-origin resource sharing) dengan dua cara,
1) Menggunakan Jsonp 2) Mengaktifkan Cors
1) Menggunakan Jsonp- untuk menggunakan Jsonp kita perlu menginstal paket nuget WebApiContrib.Formatting.Jsonp dan perlu menambahkan JsonpFormmater di screenshot referensi WebApiConfig.cs,
Kode jquery
2) Mengaktifkan Cors -
untuk mengaktifkan cors kita perlu menambahkan paket nuget Microsoft.AspNet.WebApi.Cors dan perlu mengaktifkan cors di WebApiConfig.cs lihat screenshot
Untuk referensi lebih lanjut, Anda dapat merujuk repo sampel saya di GitHub menggunakan tautan berikut. https://github.com/mahesh353/Ninject.WebAPi/tree/develop
sumber