[MVC]ASP.NET Web API遇到了JSONP

[MVC]ASP.NET Web API遇到了JSONP

今天使用Asp.NET MVC中的Web API,使用Jsonp取值時,居然直接跑到了錯誤的函式之中。

image

 

查到了「Creating a JSONP Formatter for ASP.NET Web API」,發現目前Asp.NET MVC中的Web API並沒有針對Jsonp來動態去建立CallBack的函式。

所以要自已建立一個JSONP Formatter來處理它。

以下是測試的程式,

Model

namespace MVCInActionChp5.Models
{
    public class CustomerSummary
    {
        public string Name { get; set; }
        public string Active { get; set; }
        public string ServiceLevel { get; set; }
        public string OrderCount { get; set; }
        public string MostRecentOrderDate { get; set; }
    }
}

 

測試的Web API

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

using MVCInActionChp5.Models;

namespace MVCInActionChp5.Controllers
{
    public class ApiAController : ApiController
    {
        // GET api/ApiA/10
        public List<CustomerSummary> Get(int id)
        {
            return new List<CustomerSummary>
            {
                new CustomerSummary{
                    Name = "Rainmaker" + id.ToString()
                    , Active = "Y"
                    , MostRecentOrderDate = "2012/09/11"
                    , OrderCount = "3"
                    , ServiceLevel = "100"
                }
            };
        }
    }
}

 

測試的Html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script src="Scripts/jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
    <input type="button" id="btnTestApiA"   value="TestApiA" />
    <script type="text/javascript">
        $(document).ready(function () {
            $('#btnTestApiA').bind('click', function () {
                var apiurl = "http://localhost:27063/api/apiA/10";
                $.ajax({
                    url: apiurl
                    , type: 'GET'
                    , dataType: 'jsonp'
                    , success: function (result) {
                        alert(result[0].Name);
                    },
                    error: function () {
                        alert('fail');
                    }
                });
            });
        });
        
    </script>
</body>
</html>

 

JSONP Formatter

using System;
using System.IO;
using System.Net;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Net.Http;
using Newtonsoft.Json.Converters;
using System.Web.Http;


//http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API
namespace MVCInActionChp5.Models
{
    /// <summary>
    /// Handles JsonP requests when requests are fired with text/javascript
    /// </summary>
    public class JsonpFormatter : JsonMediaTypeFormatter
    {

        public JsonpFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

            JsonpParameterName = "callback";
        }

        /// <summary>
        ///  Name of the query string parameter to look for
        ///  the jsonp function name
        /// </summary>
        public string JsonpParameterName { get; set; }

        /// <summary>
        /// Captured name of the Jsonp function that the JSON call
        /// is wrapped in. Set in GetPerRequestFormatter Instance
        /// </summary>
        private string JsonpCallbackFunction;


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

        /// <summary>
        /// Override this method to capture the Request object
        /// </summary>
        /// <param name="type"></param>
        /// <param name="request"></param>
        /// <param name="mediaType"></param>
        /// <returns></returns>
        public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
        {
            var formatter = new JsonpFormatter()
            {
                JsonpCallbackFunction = GetJsonCallbackFunction(request)
            };

            // this doesn't work unfortunately
            //formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;

            // You have to reapply any JSON.NET default serializer Customizations here    
            formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
            formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;

            return formatter;
        }


        public override Task WriteToStreamAsync(Type type, object value,
                                        Stream stream,
                                        HttpContent content,
                                        TransportContext transportContext)
        {
            if (string.IsNullOrEmpty(JsonpCallbackFunction))
                return base.WriteToStreamAsync(type, value, stream, content, transportContext);

            StreamWriter writer = null;

            // write the pre-amble
            try
            {
                writer = new StreamWriter(stream);
                writer.Write(JsonpCallbackFunction + "(");
                writer.Flush();
            }
            catch (Exception ex)
            {
                try
                {
                    if (writer != null)
                        writer.Dispose();
                }
                catch { }

                var tcs = new TaskCompletionSource<object>();
                tcs.SetException(ex);
                return tcs.Task;
            }

            return base.WriteToStreamAsync(type, value, stream, content, transportContext)
                       .ContinueWith(innerTask =>
                       {
                           if (innerTask.Status == TaskStatus.RanToCompletion)
                           {
                               writer.Write(")");
                               writer.Flush();
                           }

                       }, TaskContinuationOptions.ExecuteSynchronously)
                        .ContinueWith(innerTask =>
                        {
                            writer.Dispose();
                            return innerTask;

                        }, TaskContinuationOptions.ExecuteSynchronously)
                        .Unwrap();
        }

        /// <summary>
        /// Retrieves the Jsonp Callback function
        /// from the query string
        /// </summary>
        /// <returns></returns>
        private string GetJsonCallbackFunction(HttpRequestMessage request)
        {
            if (request.Method != HttpMethod.Get)
                return null;

            var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
            var queryVal = query[this.JsonpParameterName];

            if (string.IsNullOrEmpty(queryVal))
                return null;

            return queryVal;
        }
    }
}

 

Global.asax.cs中的Application_Start()中要多加入JsonpFormatter

GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpFormatter());

 

image

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^