F# Data: HTTP ユーティリティ
.NETライブラリにはHTTP Webリクエストを作成して送信するための
強力なAPIが用意されています。
具体的には単純な WebClient
型(MSDN を参照)や、
より柔軟な機能を持った HttpWebRequest
(MSDN を参照)があります。
しかしこれらはいずれも、HTTP POSTデータや追加のヘッダなど、
特定の引数を指定した単純なHTTPリクエストを手軽に実行することには向いていません。
F# Dataライブラリにはオーバーロードを持った4つのメソッドを含む、
単純な Http
型があります。
RequestString
と AsyncRequestString
メソッドは単純なリクエストを作成して、
同期的あるいは非同期的にリクエストを送信できます。
また Request
とその非同期バージョン AsyncRequest
を使うと
バイナリファイルを送信したり、ステータスコードや応答URL、
受信ヘッダやクッキーなど、レスポンスの詳細情報を知ることができます。
この型を使うには、まず(F# Interactiveの場合は) #r
あるいはプロジェクトで
参照の追加を行ってライブラリを参照する必要があります。
この型は FSharp.Net
名前空間にあります:
1: 2: |
#r "../../../../bin/FSharp.Data.dll" open FSharp.Data |
単純なリクエストの送信
特定のWebページをダウンロードするような単純なHTTP (GET) リクエストを送信するには、
Http.RequestString
あるいは Http.AsyncRequestString
にたった1つの引数を
指定するだけです:
1: 2: 3: 4: 5: 6: 7: |
// Webサイトのコンテンツをダウンロード Http.RequestString("http://tomasp.net") // Webサイトから非同期的にダウンロード async { let! html = Http.AsyncRequestString("http://tomasp.net") printfn "%d" html.Length } |> Async.Start |
AsyncRequestString
は RequestString
と全く同じ方法で使うことができるため、
以降では RequestString
だけを使います。
クエリ引数とヘッダ
クエリ引数は引数を含んだURLを用意するか、
query
という省略可能な引数を使って指定できます。
以下では明示的にGETメソッドであることを指定していますが、
省略した場合には自動的にGETになります:
1:
|
Http.RequestString("http://httpbin.org/get", query=["test", "foo"], httpMethod="Get") |
同じように、省略可能な引数 headers
を使うと追加のヘッダを指定できます。
このコレクションには独自のヘッダを追加できますが、
Acceptヘッダのような標準的なヘッダも含まれています
( HttpWebRequest
の場合には特定のプロパティに設定する必要があります)。
以下では The Movie Database APIを使って 「batman」という単語を検索しています。 サンプルを実行するためには登録をしてAPIキーを取得する必要があります:
1: 2: 3: 4: 5: 6: 7: 8: |
// http://www.themoviedb.org 用のAPIキー let apiKey = "<登録してキーを取得してください>" // HTTP Webリクエストを実行 Http.RequestString ( "http://api.themoviedb.org/3/search/movie", httpMethod = "GET", query = [ "api_key", apiKey; "query", "batman" ], headers = [ "Accept", "application/json" ]) |
このライブラリでは(先の例のように)単純かつチェックのない文字列ベースのAPIがサポートされていますが、
スペルミスを防ぐことができるよう、あらかじめ定義されたヘッダ名を使うこともできます。
名前付きヘッダは HttpRequestHeaders
(および HttpResponseHeaders
) モジュールで定義されているため、
HttpRequestHeaders.Accept
のようにフルネームを指定するか、
以下の例のようにモジュールをオープンしておいてから Accept
のような短い名前で指定することになります。
同様に、 HttpContentTypes
列挙体には既知のコンテンツタイプ名が定義されています:
1: 2: 3: 4: 5: 6: 7: |
open FSharp.Data.HttpRequestHeaders // HTTP Webリクエストを実行 Http.RequestString ( "http://api.themoviedb.org/3/search/movie", query = [ "api_key", apiKey; "query", "batman" ], headers = [ Accept HttpContentTypes.Json ]) |
特別な情報の取得
先のコードスニペットでは、正しいAPIキーを指定しなかった場合には(401)認証エラーが返され、
例外が発生することに注意してください。
ただし WebRequest
を直接使用した場合とは異なり、例外メッセージには返されたコンテンツの情報も含まれるため、
サーバーが特別な情報を返すような場合であってもF# Interactiveで簡単にデバッグできます。
また silentHttpErrors
引数を設定することによって例外を無効化することもできます:
1:
|
Http.RequestString("http://api.themoviedb.org/3/search/movie", silentHttpErrors = true) |
この結果は以下のようになります:
"{"status_code":7,"status_message":"Invalid API key: You must be granted a valid key."}"
この場合にはHTTPステータスコードを確認すればよいため、実際にレスポンスとして返されたデータと
エラーメッセージとを混同することもないでしょう。
ステータスコードやレスポンスヘッダー、返されたクッキー、レスポンスURL
(リダイレクトされた場合にはリクエストを投げたURLとは別の値が返されることがあります)など、
レスポンスの詳細を確認したい場合には RequestString
の代わりに Request
メソッドを使用します:
1: 2: 3: 4: 5: 6: 7: |
let response = Http.Request("http://api.themoviedb.org/3/search/movie", silentHttpErrors = true) // レスポンスの詳細を確認する response.Headers response.Cookies response.ResponseUrl response.StatusCode |
リクエストデータの送信
HTTP POSTデータを含んだPOSTリクエストを作成したい場合は、
オプション引数 body
に追加データを指定するだけです。
この引数は3つのケースを持った判別共用体 HttpRequestBody
型です:
TextRequest
はリクエストの本体で文字列を送信するために使用しますBinaryUpload
はリクエストにバイナリデータを含めて送信する場合に使用しますFormValues
は特定のフォームの値を名前と値のペアとして 送信するために使用します
bodyを指定した場合、引数 httpMethod
には自動的に Post
が設定されるようになるため、
明示的に指定する必要はありません。
以下ではリクエストの詳細を返すサービス httpbin.org を使っています:
1:
|
Http.RequestString("http://httpbin.org/post", body = FormValues ["test", "foo"]) |
デフォルトでは Content-Type
ヘッダには HttpRequestBody
に指定した値に応じて
text/plain
application/x-www-form-urlencoded
application/octet-stream
のいずれかが設定されます。
ただしオプション引数 headers
を使ってヘッダのリストに content-type
を
追加することでこの動作を変更できます:
1: 2: 3: 4: |
Http.RequestString ( "http://httpbin.org/post", headers = [ ContentType HttpContentTypes.Json ], body = TextRequest """ {"test": 42} """) |
リクエスト間でクッキーを管理する
リクエスト間でクッキーを管理したい場合には、
引数 cookieContainer
を指定します。
以下では HttpRequest
クラスに関するMSDNドキュメントをリクエストしています。
そうするとF#ではなくC#のコードスニペットが表示されます:
1: 2: 3: 4: 5: 6: 7: 8: |
// 特定のクラスに関するドキュメント用のURLを用意する let msdnUrl className = let root = "http://msdn.microsoft.com" sprintf "%s/en-gb/library/%s.aspx" root className // ページを取得してF#コードを検索する let docInCSharp = Http.RequestString(msdnUrl "system.web.httprequest") docInCSharp.Contains "<a>F#</a>" |
別のMSDNのページに移動してF#のコード例をクリックしてから
HttpRequest
クラスのドキュメントに戻ってくると、
同じ cookieContainer
を保持し続けている限りはF#のコードが
表示されるようになります:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: |
open System.Net let cc = CookieContainer() // 言語を切り替えるためのリクエストを送信 Http.RequestString ( msdnUrl "system.datetime", query = ["cs-save-lang", "1"; "cs-lang","fsharp"], cookieContainer = cc) |> ignore // 再度ドキュメントをリクエストしてF#のコードを検索 let docInFSharp = Http.RequestString ( msdnUrl "system.web.httprequest", cookieContainer = cc ) docInFSharp.Contains "<a>F#</a>" |
バイナリデータの送信
RequestString
メソッドでは常に string
としてレスポンスが返されます。
しかし Request
メソッドの場合にはレスポンスの content-type
ヘッダに応じて
HttpResponseBody.Text
または HttpResponseBody.Binary
が返されます:
1: 2: 3: 4: 5: 6: |
let logoUrl = "https://raw.github.com/fsharp/FSharp.Data/master/misc/logo.png" match Http.Request(logoUrl).Body with | Text text -> printfn "Got text content: %s" text | Binary bytes -> printfn "Got %d bytes of binary content" bytes.Length |
HTTPリクエストのカスタマイズ
ライブラリに元々備えられていないような機能を使用したい場合には、
customizeHttpRequest
引数を設定することになります。
この引数には HttpWebRequest
を変更するための関数を指定します。
たとえばリクエストにクライアント証明書を追加したいとします。
そのためにはまず System.Security.Cryptography
以下の
X509Certificates
名前空間をオープンして、
X509ClientCertificate2
の値を用意し、
この値をリクエストの ClientCertificates
リストに追加します。
証明書が myCertificate.pfx
に格納されているとすると、
以下のようなコードになります:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: |
open System.Security.Cryptography.X509Certificates // ローカルファイルから証明書を読み取り let clientCert = new X509Certificate2(".\myCertificate.pfx", "password") // 証明書付きでリクエストを送信 Http.Request ( "http://yourprotectedresouce.com/data", customizeHttpRequest = fun req -> req.ClientCertificates.Add(clientCert) |> ignore; req) |
関連する記事
- API リファレンス: HTTP クラス
- API リファレンス: HttpMethod モジュール
- API リファレンス: HttpRequestHeaders モジュール
- API リファレンス: HttpResponseHeaders モジュール
- API リファレンス: HttpContentTypes モジュール
- API リファレンス: HttpRequestBody 判別共用体
- API リファレンス: HttpResponse レコード
- API リファレンス: HttpResponseBody 判別共用体
- API リファレンス: HttpResponseWithStream レコード
private new : unit -> Http
static member private AppendQueryToUrl : url:string * query:(string * string) list -> string
static member AsyncRequest : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> Async<HttpResponse>
static member AsyncRequestStream : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> Async<HttpResponseWithStream>
static member AsyncRequestString : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> Async<string>
static member private InnerRequest : url:string * toHttpResponse:(string -> int -> string -> string -> 'a0 option -> Map<string,string> -> Map<string,string> -> MemoryStream -> Async<'a1>) * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?responseEncodingOverride:'a0 * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> Async<'a1>
static member Request : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> HttpResponse
static member RequestStream : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> HttpResponseWithStream
static member RequestString : url:string * ?query:(string * string) list * ?headers:(string * string) list * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:(string * string) list * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) -> string
Full name: FSharp.Data.Http
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken
Full name: Microsoft.FSharp.Control.Async
--------------------
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
Full name: Http.apiKey
from FSharp.Data
Full name: FSharp.Data.HttpRequestHeaders.Accept
from FSharp.Data
Full name: FSharp.Data.HttpContentTypes.Json
Full name: Http.response
Full name: FSharp.Data.HttpRequestHeaders.ContentType
Full name: Http.msdnUrl
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Full name: Http.docInCSharp
Full name: Http.cc
type CookieContainer =
new : unit -> CookieContainer + 2 overloads
member Add : cookie:Cookie -> unit + 3 overloads
member Capacity : int with get, set
member Count : int
member GetCookieHeader : uri:Uri -> string
member GetCookies : uri:Uri -> CookieCollection
member MaxCookieSize : int with get, set
member PerDomainCapacity : int with get, set
member SetCookies : uri:Uri * cookieHeader:string -> unit
static val DefaultCookieLimit : int
...
Full name: System.Net.CookieContainer
--------------------
CookieContainer() : unit
CookieContainer(capacity: int) : unit
CookieContainer(capacity: int, perDomainCapacity: int, maxCookieSize: int) : unit
Full name: Microsoft.FSharp.Core.Operators.ignore
Full name: Http.docInFSharp
Full name: Http.logoUrl
Full name: Http.clientCert
type X509Certificate2 =
inherit X509Certificate
new : unit -> X509Certificate2 + 12 overloads
member Archived : bool with get, set
member Extensions : X509ExtensionCollection
member FriendlyName : string with get, set
member GetNameInfo : nameType:X509NameType * forIssuer:bool -> string
member HasPrivateKey : bool
member Import : rawData:byte[] -> unit + 5 overloads
member IssuerName : X500DistinguishedName
member NotAfter : DateTime
member NotBefore : DateTime
...
Full name: System.Security.Cryptography.X509Certificates.X509Certificate2
--------------------
X509Certificate2() : unit
(+0 other overloads)
X509Certificate2(rawData: byte []) : unit
(+0 other overloads)
X509Certificate2(fileName: string) : unit
(+0 other overloads)
X509Certificate2(handle: nativeint) : unit
(+0 other overloads)
X509Certificate2(certificate: X509Certificate) : unit
(+0 other overloads)
X509Certificate2(rawData: byte [], password: string) : unit
(+0 other overloads)
X509Certificate2(rawData: byte [], password: System.Security.SecureString) : unit
(+0 other overloads)
X509Certificate2(fileName: string, password: string) : unit
(+0 other overloads)
X509Certificate2(fileName: string, password: System.Security.SecureString) : unit
(+0 other overloads)
X509Certificate2(rawData: byte [], password: string, keyStorageFlags: X509KeyStorageFlags) : unit
(+0 other overloads)