【F# + ASP.NET Web API】 JSONPでクロス ドメイン通信を行うには

◆ 参考

JSONPってなに?JSONP with ASP.NET Web API

◆ JSONP形式へ変換

namespace Sample.Web

open System
open System.IO
open System.Web
open System.Net
open System.Net.Http
open System.Net.Http.Formatting
open System.Net.Http.Headers
open System.Threading.Tasks

// JSONP形式へ変換
type JsonpMediaTypeFormatter() =
    inherit JsonMediaTypeFormatter()

    do
        base.SupportedMediaTypes.Add(JsonMediaTypeFormatter.DefaultMediaType)
        base.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"))
        base.MediaTypeMappings.Add(new UriPathExtensionMapping(
            "jsonp", JsonMediaTypeFormatter.DefaultMediaType))
    
    // コールバック・パラメータ(callback)
    let mutable _callbackQueryParameter = String.Empty
    member x.CallbackQueryParameter
        with get() = 
            if (_callbackQueryParameter.Equals(String.Empty)) then
                "callback"
            else
                _callbackQueryParameter
        and set v = _callbackQueryParameter <- v

    member val callback = String.Empty with get, set

    // URLにクエリ文字列「callback」が存在するか確認
    member x.IsJsonpRequest() =
        let test = HttpContext.Current.Request.HttpMethod

        // HTTP GET 以外の場合は、処理終了
        if (HttpContext.Current.Request.HttpMethod <> "GET") then
            false
        else
            // クエリ文字列「callback」を取得
            x.callback <- HttpContext.Current.Request.QueryString
                .[x.CallbackQueryParameter]            

            not(String.IsNullOrEmpty(x.callback))        

    member x.BaseWriteToStreamAsync(
        typeValue:Type, value:obj, writeStream:Stream
        ,content:HttpContent ,transportContext:TransportContext) =

        base.WriteToStreamAsync(
            typeValue, value, writeStream
            , content, transportContext).Wait()

    override x.WriteToStreamAsync(
        typeValue:Type, value:obj , writeStream:Stream 
        ,content:HttpContent ,transportContext:TransportContext) =

        if (x.IsJsonpRequest()) then
            // URLにクエリ文字列「callback」が存在する場合         
            Task.Factory.StartNew(fun () ->
                // JSONP形式 = "コールバック関数(JOSNデータ)" に変換
                use writer = new StreamWriter(writeStream)
                writer.Write(x.callback + "(")
                writer.Flush()
                x.BaseWriteToStreamAsync(typeValue, value
                    , writeStream, content, transportContext)
                writer.Write(")")
                writer.Flush()
            )            
        else
            base.WriteToStreamAsync(typeValue, value
                , writeStream, content, transportContext)

◆ Global.fs

namespace Sample.Web

open System
open System.Web
open System.Web.Mvc
open System.Web.Routing
open System.Web.Http
open System.Data.Entity

type Route = { controller : string
               action : string
               id : UrlParameter }

type MapHttpRouteSettings = { id : obj }

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterGlobalFilters(filters:GlobalFilterCollection) =
        filters.Add(new HandleErrorAttribute())

    static member RegisterRoutes(routes:RouteCollection) =
        // 省略

    member this.Start() =
        AreaRegistration.RegisterAllAreas()
        
        // JSONP 形式変換を処理を適用
        let config = GlobalConfiguration.Configuration
        config.Formatters.Insert(0, new  JsonpMediaTypeFormatter())
        
        Global.RegisterGlobalFilters(GlobalFilters.Filters)
        Global.RegisterRoutes(RouteTable.Routes)

◆ Controller

namespace Sample.Web.Controllers

open System
open System.Web
open System.Web.Mvc
open System.Net.Http
open System.Web.Http
open System.Collections.Generic

// #r "System.Runtime.Serialization"
open System.Runtime.Serialization

// MongoDB 関連
open  MongoDB.Driver;
open  MongoDB.Driver.Builders
open  MongoDB.Bson
open  MongoDB.Bson.Serialization.Attributes

// JSONの型
[<DataContract>]
type SampleEntity() =
    [<BsonId>]
    member val _id = ObjectId.GenerateNewId() with get, set
    [<DataMember>]
    member val Name = String.Empty with get, set
    [<DataMember>]
    member val Age = Int32.MinValue with get, set
    
type SampleController() =
    inherit ApiController()

    member x.Get(name:string) = 
        // MongoDBへ接続
        let server = MongoServer.Create("mongodb://localhost/?safe=true")
        let db = server.GetDatabase("SampleDB")
        let collection = db.GetCollection<SampleEntity>("SampleTable")

        // 条件を指定して、ドキュメントを取得        
        let query = new QueryDocument();
        query.Add("Name", BsonString.Create(name))        
        |> ignore

        collection.Find(query)

◆ HTML(jQuery)

$(function () {
    $.ajax({            
        type: "GET",
        url: 'http://localhost:54465/api/Sample/Taro' + "?callback=?",
        data: {},
        dataType: 'jsonp',
        jsonp: 'jsoncallback',
        success: function (data) {
            //通信が成功した場合の処理 (JSONPのコールバック関数はこれが呼ばれます)
            alert('success');
        },
        error: function (data, status) {
            //通信終了時の処理
            alert('error');
        },
        complete: function (data) {
            //通信終了時の処理
            alert('complete');
        },
    });
});

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中