前のページへ

サーバ編

次のページ





RESTfulウェブ・サービスと(RESTful web services)

ウェブ・サービスの世界ではREST (Representational State Transfer)というコンセプトが良く使われる。YahooGoogleFacebookなどのウェブ・サービスはSOAP ベースやWSDL ベースのインターフェースを非推奨あるいは不使用としており、もっぱら使いやすいRESTモデルを使ってJSONファイルによるサービスを公開している。

RESTベースでJSONファイルを交わすREST-JSONは、これまでのSOAPベースでXMLファイルを交わすSOAP-XMLに比べて、次のような利点を有する:

  • サイズ: SOAP-XMLに比べずっとコンパクトであり、ネットワーク上で渡すデータ量が少なく、特にスマートフォンなどのモバイル・アプリケーションにはこれが重要である。

  • 効率: REST-JSONは構文解析が容易なので、データの抽出と変換が容易である為、クライアントのCPU負荷がずっと軽くなる。

  • キャッシュ: キャッシュに対応しているので、応答時間とサーバ・ロード時間が改善される。

  • 実装: REST-JSONのインターフェイスは設計と実装がより容易である。

SOAP-XMLは一般に大量のテキストを交換するときや、銀行などのセキュアなサービスで使われている。

本章では、Dartが現時点でクライアント側およびサーバ側でRESTベースのウェブ・サービスにどのように対応しているかを紹介する。

下表は現時点で用意されているDart 2対応のライブラリの主要なものであるDart 2非対応のライブラリが数多く存在するがいずれ対応されることが期待される)

名称

形態

用途

概要

作者

JsonCodec

JsonEncoder

JsonDecoder

dart:convert

双方

JSONコーデック

Google

json_serializable

pub

双方

コード生成パッケージ


json_annotation

pub


JSONシリアライザのアノテーション

Google

googleapis

pub

クライアント

Googleのウェブ・サービス・アクセスの為のAPIサンプル

Google

uri

pub

双方

UriTemplate対応ライブラリ

Google

jsonp

pub

クライアント

JSONP要求作成作業の簡素化

Matthew Franglen

built_value

pub

双方

json_serializableと等価

David Morgan























RESTスタイルの概説

REST (Representational State Transfer) とは、コンピュータ科学者のRoy Thomas Fielding2000年に自分の博士論文の中で提唱したネットワーク・ベースのアプリケーション・ソフトウエアの指針となるアーキテクチャ・スタイルである。RESTだけだとアメリカでは公衆便所(正式にはRest Roomだろうが)になって仕舞うので、通常はこのスタイルをきちんと実装したという意味でRESTfulというように、形容詞として良く使われる。

本節では、Fieldingが提案したRESTの概説と、実際のウェブ・サービスに於けるRESTfulサービスの基礎を説明する。

Fieldingの提案

Roy Thomas Fielding1965California生まれのアメリカ人で、California州立大学Irvine校の情報とコンピュータ科学科修士課程にいたときからウェブの標準策定に関わった。丁度このころが世界の大学や研究機関が開発の中心だったインターネットの世界でのウェブの誕生・発展期であり、非常に良い時代と環境に恵まれていたことになる。彼はこの大学で2000年に博士号を取得している。日本でも慶応大学、大阪大学、東京大学等がインターネットの日本での普及に大きく寄与したが、標準化には殆ど寄与できなかったことは反省すべきである。彼の標準化での貢献は、特に1996年のHTTP 1.0 プロトコル(RFC 1945)及び1999年のHTTP 1.1 プロトコル(RFC 2616)で大きい。HTTPはウェブの発明者ともいわれるTim Berners-Leeが考え出したプロトコルである。彼がBerners-Leeなどとともに策定したHTTPプロトコルはHTTP 1.1で強化され、現在もそのまま使われている。HTTPに関しては、筆者の「改定サーブレット・チュートリアル」の2.4節を参照のこと。彼はまたW3C (World Wide Web Consortium)でのHTMLURI (RFC 1808, 2396)の標準化にも貢献した。彼はまたApache HTTPサーバ・プロジェクトの設立メンバのひとりであり、1999年から初代会長を3年間務めた。現在もASF (The Apache Software Foundation)の役員のひとりである。1999年にMITTechnology Review誌の「トップ100人の35歳以下の若手革新者たち」初年度に彼が指名されている。

その彼の成果に対するご褒美としての博士論文「ネットワーク・ベースのソフトウエア・アーキテクチャのアーキテクチャ・スタイルと設計(Architectural Styles and the Design of Network-based Software Architectures)は、自分がこれまのウェブでの貢献を整理するにあたり、その基本となってきたコンセプトとしてRESTという基本原則を示して、博士論文らしくしようとした(あるいは論理武装しようとした?)ものと推察される。これはハイパーメディア上の構成要素たちが、リソースの表現(Representation)を使ってそのリソースのある状態(State)を転送(Transfer)しあうという基本コンセプトである。

このRESTという原則がウェブ・サービスの世界で注目され、その使い易さ(例えばサーバはクライアント間の状態を保持しない)からこれまでのCORBAあるいはSOAP / WSDLベースのインターフェイス設計を凌駕するほどになっている。これは上位のアプリケーション・プロトコルから下位の通信・プロトコルであるHTTPへの回帰現象ともいえよう。現在多くのウェブ・サービスのプロバイダたちが、自分のサービスの為のRESTベースのAPI(ここでいるAPIというのは、URLの一部としてサーバにパラメタを渡すWeb APIの仕様のこと)を提供している。またJCP (Java Community Process)ではJSR 311 (JAX-RS: The JavaTM API for RESTful Web Services)という仕様書が出されており、Java EE6に組み入れられている。

彼が主張しようとしていることは、彼の1998年のプレゼンテーション資料のアブストラクトにより良く示されている:

アーキテクチャはあるスタイルの実体化物(インスタンス)とも言え、たとえばWWW技術の世界ではURL, HTTP, HTML, Java applets等がこれにあたる。スタイルというのはアーキテクチャたちのなかで共通したパタンのことをいう。スタイルとしては、例えばパイプとフィルタ、リモート・セッション、イベント・ベースの統合(暗示的呼び出し)、クライアント/サーバ(明示的呼び出し)、分散オブジェクト、及び多種の分散プロセスのパラダイムなどが使われてきている。これらのスタイルの各々は要素間の通信の特定のパラダイムに最適化するように意図されている。

彼の考えでは、ウェブのアーキテクチャ・スタイルは以下の5つの基本的概念が中心になっている

  • リソース

  • リソースの表現(representation)

  • 表現の取得/修正の為の通信

  • アプリケーションの状態(state)のインスタンスとしてのウェブ「ページ」

  • ある状態から次の状態に移動する為のエンジン

    • ブラウザ(もっとも一般的に使われている)

    • スパイダ

    • なんらかのメディア・タイプのハンドラ

WWWは自分がRESTと称する新しいアーキテクチャのスタイルへと発展したと彼はいう。このスタイルはクライアント/サーバ、パイプとフィルタ、及び分散オブジェクトのパラダイムたちの要素たちを使うことで、あるリソースの表現のネットワーク転送を最適化する。ウェブ・ベースのアプリケーションというのは、状態表現(ページ)と状態間の潜在的転移(リンク)のダイナミックなグラフだと見ることが出来る。それがもたらされる結果は、サーバの実装とクライアントのリソースの認知との分離、大きなクライアント数に十分拡張できる、無制限の規模とタイプのストリーム・データの転送ができる、データ転送とキャッシングの要素のような仲介者(プロキシとゲートウェイ)に対応できる、そしてユーザ・エージェント要素内でアプリケーションの状態に集中した、アーキテクチャであると彼はいう

彼がRESTをどのように導き出したかは、彼の博士論文の第5章の最初の部分(5.1節)で説明されている。彼は全く空のスタイル(null style)に以下のような幾つかの制約を付加することで、RESTアーキテクチャ・スタイルを導入している:

  • クライアント-サーバのアーキテクチャ・スタイル:ユーザ・インターフェイスとデータ蓄積を分離することでユーザ・インターフェイスのプラットフォーム間での可搬性(portability)が高まり、またサーバが簡素化され拡張性が高まる

  • 状態なし(ステートレス)のアーキテクチャ・スタイル:通信に状態を持たせない。各クライアントからサーバへの要求は、その要求を理解する為の総ての情報を持たせる。セッション状態はクライアント上でのみ保持される。これにより可視性(例えば監視システムはその要求を調べるだけで良い)、信頼性(部分的な障害からの復旧が簡単)、及び拡張性(サーバ側で状態情報を保持しなくても良い)が得られる

  • キャッシュ付加可能なスタイル:ある要求に対する応答にキャッシュできるかどうかを明示的あるいは暗示的ラベルを付加することで、効率、拡張性、ユーザが受け取る感じを改善できる

  • インターフェイスが統一されたスタイル:ハイパー・メディアを構成する各要素間のインターフェイスを統一する。これにより情報が標準化された形式で転送されるので、システム全体のアーキテクチャが簡素化され、また要素のインターフェイスの一般化がなされる

  • 階層化されたシステムのアーキテクチャ・スタイル:大規模なインターネットに対応できるよう、階層化されたシステムのアーキテクチャ・パタンとする:各要素は自分が属する階層以外の階層が不可視になることで、大規模化に対応できるようになる

  • コード・オン・デマンド:アプレットやスクリプトなどの形式で実行コードがダウンロードできるようにする。これによりクライアント側が簡素化される。また、システムの拡張性が得られる。但しこれは可視性を阻害することになるので、RESTの中ではオプショナルな制約となっている

RESTにおけるデータ要素たちを纏めると次の表のようになる。これらはHTTPプロトコルを理解していれば理解が早い(筆者の改定サーブレット・チュートリアルの第2章参照のこと):

RESTのデータ要素

データ要素

ウェブでの事例

リソース

ハイパーテキスト参照の意図されたコンセプト上のターゲット

リソース識別子

URLURN

表現

JSONXMLHTMLなどのドキュメント、JPEGイメージ

表現のメタデータ

メディア・タイプ(media type)、前回変更時刻(last-modified time)

リソースのメタデータ

ソースのリンク(source link)、代替(alternates)、変動(very)

制御データ

if-modified-sincecache-control

リソース

リソースとはRESTにおける「情報」の重要な抽象化である。リソースは名前(識別子、実際にはURI: Uniform Resource Identifier)をもったもので、ドキュメントまたはイメージ、例えば「今日の東京の天気」といったサービス、他のリソースたちの集合、あるいは「人々」といったネットワークされていないオブジェクト、などである。URLなどで表されるリソースが指し示すのは特定のドキュメントやイメージではなく概念である、という考え方である。リソースはある実体のセットへのコンセプト的なマッピングであり、ある特定の時刻におけるそのマッピングに対応した実体のことではない。即ちリソースは時間の関数として実体のセットあるいはそれと等価な値たちを持つ。あるセットのなかの値たちは、リソース表現(resource representations)あるいは/及びリソース識別子たち(resource identifiers)ということになる。

RESTでは、要素間の係わり合いのなかで関与される特定のリソースを特定する為に、リソース識別子(resource identifier)が使われる。HTTPでいえばURIがこれに相当する。

リソースの表現

RESTの構成部品たちは、そのリソースの現在のあるいは意図した状態(state)を捕捉するのに表現(representation)を使い、またその部品(components)間でその表現を転送(transfer)することで、あるリソースにたいするアクションを行う。RESTの構成部品たちは、そのリソースに直接関わりあおうことは無く、あくまでもその表現を介すことになる。表現はバイト列、及びそれに加えてそれらのバイトを記述する為の表現メタデータ(通常は名前と値のペアたちで構成)である。彼はウェブはリソースの表現を操作し転送するよう設計されているという。例えばHTTPではヘッダ部分がメタデータ部分であり、ボディ部分はバイト列用(通常はテキスト/HTMLMIMEなどのタイプで)として使用されている:

  • 単一のリソースは複数の表現に結び付けられていても良い(コンテント交渉)

  • 表現のデータ・タイプはメディア・タイプとして知られているものである(例えばMIMEなど)

    • このリソースの情報を提供

  • ハイパーメディア認知のメディア・タイプをとる

    • 潜在的な状態遷移を提供する

  • 殆どの表現はキャッシュできる

ウェブ・サービスの世界に於けるREST

Fieldingは自分のインターネットの世界での貢献のベースとなったコンセプトをRESTという言葉で示したが、ウェブ・サービスの世界の人たちはこのコンセプトをより自分なりにより狭く解釈している。従ってその定義はとくに標準化されている訳ではない。一般的にはFieldingの定義(「RESTの導入」の項で述べたクライアント / サーバ、ステートレス、キャッシュ対応、階層化などの制約)に対し、更に以下のものが付加されている:

  • HTTPベースである

    RESTの導入にあたってFieldingはインターフェイスの統一という制約を加えたが、ウェブ・サービスの世界ではHTTPがインターフェイスのベースとなっている。従ってGETPOSTPUTDELETEなどのHTTPメソッドを使ってクライアントはサーバにアクセスする。これらのメソッドは厳格に区別して使用される:

    • GET:取得(GET要求でリソースに何らかの影響を与える使い方はしてはならない)

    • POST:新規作成

    • PUT:更新

    • DELETE:削除

    これらはしばしばCRUD (Create, Read, Update, Delete)操作と呼ばれる

  • 従ってリソースの表現に対する要求はHTTPの要求行のURIからのみとなる

    HTTPに関しては別途説明するが、HTTPの他のヘッダ行を使って要求先を指定してはならない。逆に言えばリソースはURIを介してのみ到達可能である(但しPOSTなどで送信されるデータはHTTPメッセージのヘッダ行とボディ部分が関与する)

  • 表現には一般的な標準であるデータ・フォーマットを使用する

    XMLHTMLAtomRSSJSONCSVGIFなど

    特にJSONが多用されていて(REST-JSON方式)、RESTといえばJSONだと一般的に理解されるまでになっている。

  • MIMEタイプとしては以下のものが使われる:

    • text/xml

    • text/html

    • application/json

    • image/gif

    • image/jpeg

      等々

  • ハイパーリンクを使ってリソース間の関連(連鎖)を伝えたり、アプリケーションの状態遷移(一連のアクションに於ける関連ステップのことで、クライアントとの間の状態遷移とは意味が異なる)をクライアントが選択する手段を伝える。状態を制御・維持するのはクライアントである

    これは「連結性(Connectedness))」とも呼ばれる。あらゆるRESTベースのシステムは、クライアントが関連リソースにアクセスする必要があることを前提としており、リソース表現に関連リソースを含めてクライアントに返す。

RESTfulなウェブ・サービスでは既存の良く知られたW3CIETFの標準たち(HTTP, XML, URI, MIME)が使われており、また最小限のツーリングでウェブ・サービスを構築できるので、RESTfulなウェブ・サービスの開発コストが低くて済み、従って導入の障壁が非常に低い。RESTfulなウェブ・サービスの開発にはEclipseのような統合開発環境が使え、開発がより簡単化される。

OracleJava EE6チュートリアルでは、以下のような条件が満たされるときが、RESTfulなウェブ・サービスを設計するのが適正な場合であろうと書いている:

  • そのウェブ・サービスが完全にステートレスであるとき。そのリソースへの係わり合いが、サーバの再起動でも影響を受けないようにできるかどうかがひとつの判断基準となる。

  • キャッシュによってサービス性能が改善できるとき。そのウェブ・サービスが返すデータが動的に生成されたものでなくキャッシュ可能な場合は、ウェブ・サーバたちや仲介サーバたちが提供するキャッシングにより性能を改善できる。但し殆どのサーバにとってはキャッシュはHTTP GET要求のみに制限されているので、開発には注意が必要である。

  • サービスの提供側と消費側が互いに渡すデータのコンテキストと中身を理解しているとき。ウェブ・サービスのインターフェイスを記述する公式な方法が無い為、双方は別途交換されているデータを記述するスキーム、及びそれを有意義な形で処理する手段で同意がとられていなければならない。実際には、RESTful実装としている殆どの商用のアプリケーションでは、一般的なプログラミング言語でそのインターフェイスを記述したいわゆる付加価値ツールキットを提供している。

  • 帯域が特に重要であって、帯域制限の必要がある場合。PDAや携帯電話機のようにプロファイル制限があって、XMLペイロード上でのSOAP要素たちのヘッダや付加的レイヤのオーバヘッドが問題となる機器にとって、RESTは特に有用である。

  • RESTfulスタイルにより、既存のウェブのサイトたちにウェブ・サービスのデリバリ(提供)あるいはアグレゲーション(統合)が容易になる。開発者たちはJAX-RS及びAJAX (Asynchronous JavaScript with XML)のような技術が使えるし、自分たちのウェブ・アプリケーションの中でそのサービスを使うのにDWR (Direct Web Remoting)のようなツールキットを使うことが出来る。白紙から始めるのではなく、既存のウェブ・サイトのアーキテクチャを大きく変えることなく、サービスはXMLで表現され、HTMLページで消費されるようにできる。既存の開発者たちは、新しい技術で最初から始めるのではなく、既に馴染みがあるものを付加することになるので、彼らはより生産的になる(仕事がはかどる)



簡単なサンプルでその構成を知る

Dartチームはその教材として簡単なサンプルを用意している。従ってこれを使ったクライアントとサーバのコードを試してみよう。

GET要求を使った例

最初にHTML formからGET要求をする number_thinker.dart(サーバ)とmake_a_guess.html(クライアント)を試す。

  1. 教材のサンプル集のリポジトリからをZIPファイルダウンロードし、解凍する。

  2. 解凍するとDownloads\dart-tutorials-samples-master\dart-tutorials-samplesというフォルダをIDEで開く。




  3. number_thinker.dartをサーバとして走らせ、make_a_guess.htmlのファイルパスをコピーしてブラウザのアドレスバーに貼り付けて開く、または localhost:8080/make_a_guess.htmlでこのファイルを呼び出す:




  4. 適当な数を選択してGuessボタンをクリックするとサーバが当たりかどうかを返してくる




このアプリではRESTに準拠している。ここでクライアントからのGET要求は以下の目的で使用される:

  • データの取り出しにのみ使用される

  • サーバの状態を変えない

  • 長さの制限がある

  • URLのなかでクエリ文字列(この例では?q=1)として要求が送れる

この例ではREST準拠のGET要求がされている

POST要求を使った例

ここではbasic_writer_server.dartbasic_writer_client.dartを使用する。このアプリはあるサーバが別のサーバに対しRESTベースでデータを送受する用途を想定している。

  1. file/txtファイルを開き、何かが残っていたらそれを消去する

  2. 最初にbasic_writer_server.dartを走らせる。

  3. 次にbasic_writer_client.dartを走らせる:




  4. またfile/txtファイルには次のようなJSONテキストが残される:

{"name":"Han Solo","job":"reluctant hero","BFF":"Chewbacca",
"ship":"Millennium Falcon","weakness":"smuggling debts"}

このようにクライアントがあるJSONデータを送信するとサーバはこれをfile/txtファイルに記録し

Wrote data for Han Solo.

とコンソールに表示する。

このクライアントはHttpClientオブジェクトを生成し、要求をするのにpost()メッソッドを使っている。要求をするために2つのFutureが関与している:

  • post()メソッドはサーバとのネットワーク接続を確立し最初のFutureで完了する、こちらのFutureHttpClientRequestオブジェクトを返す。

  • クライアントは要求オブジェクトを組み立ててそれを閉じる。close()メソッドはその要求をサーバに送信し、2番目のFutureを返す。このFutureHttpClientResponseオブジェクトで完了する。

Future main() async {
  HttpClientRequest request = await HttpClient().post(_host, 4049, path) /*1*/
    ..headers.contentType = ContentType.json /*2*/
    ..write(jsonEncode(jsonData)); /*3*/
  HttpClientResponse response = await request.close(); /*4*/
  await response.transform(utf8.decoder /*5*/).forEach(print);
}

basic_writer_client.dart

/*1*/ post()メソッドはhostport、及び要求しているリソースへのパスを必要とする。HttpClientクラスはpost()に加えて、postUrl()get()、及びopen()等の要求をするメソッドを有している。

/*2*/ HttpClientRequestのオブジェクトは要求ヘッダたちを指定するHttpHeadersオブジェクトを有している。 contentTypeのようなヘッダはそのヘッダに固有なプロパティたちを有している。他のヘッダたちに対しては、HttpHeadersのなかに置くためのset()メソッドを使用する。

/*3*/ このクライアントはrequestオブジェクトにデータを書くのにwrite()を使っている。JSONというエンコーディングは ContentTypeヘッダのなかで指定したタイプに合ったものである。

/*4*/ close()メソッドはこの要求をサーバに送信し、それが完了したら HttpClientResponseオブジェクトを返す

/*5*/ サーバからのUTF-8応答はデコードされる。このデータを通常のDartの文字列の型に変換するには dart:convertライブラリのなかで定義されているtransformerを使用する。

RESTfulPOST要求

GET要求と同じく、RESTではPOST要求に対するガイドラインが存在する。

POST要求は:

  • リソースを生成する(この場合はファイル)

  • ファイルとディレクトリのパス名と同じような構造を持つURIを使う;例えばこのURIはクエリ文字列を持っていない

  • データはJSONまたはXMLとして転送する

  • 長さに制限はない。

このサンプルでのクライアントはREST対応のPOST要求をしている。

サーバ内でのPOST要求の処理

HttpRequestオブジェクトはバイトのリスト(Stream<List<int>>)である。クライアントからのデータを取得するには HttpRequestオブジェクト上でのデータをリスンする。クライアントからののデータが大量のデータでできているときは、そのデータは複数のチャンク(かたまり)たちで到着しうる。これらのチャンクの文字列の値を連結するにはStreamjoin()メソッドが使える。




basic_writer_server.dartファイルはましくこのパタンに従ったサーバを構成している。

String _host = InternetAddress.loopbackIPv4.host;
Future main() async {
  var server = await HttpServer.bind(_host, 4049);
  await for (var req in server) {
    ContentType contentType = req.headers.contentType;
    HttpResponse response = req.response;
    if (req.method == 'POST' &&
        contentType?.mimeType == 'application/json' /*1*/) {
      try {
        String content =
            await req.transform(utf8.decoder).join(); /*2*/
        var data = jsonDecode(content) as Map; /*3*/
        var fileName = req.uri.pathSegments.last; /*4*/
        await File(fileName)
            .writeAsString(content, mode: FileMode.write);
        req.response
          ..statusCode = HttpStatus.ok
          ..write('Wrote data for ${data['name']}.');
      } catch (e) {
        response
          ..statusCode = HttpStatus.internalServerError
          ..write("Exception during file I/O: $e.");
      }
    } else {
      response
        ..statusCode = HttpStatus.methodNotAllowed
        ..write("Unsupported request: ${req.method}.");
    }
    response.close();
  }
}

/*1*/ この要求はHttpHeadersオブジェクトを持っている。クライアントが contentTypeヘッダにJSON (application/json)をセットしたことを思いだして頂きたい。このサーバは JSONエンコードでない要求は排除している。

/*2*/ POST要求は送信できるデータ量に制限がなく、複数のチャンクとして送信してくるかもしれない。更に、JSONUTF-8であり、UTF-8文字は複数のバイトとしてエンコードされ得る。join()メソッドがこれらのチャンクを連結してくれる。

/*3*/ クライアントからのデータはJSONフォーマットである。サーバは dart:convertライブラリにある JSON codecを使ってデコードする。

/*4*/ 要求からのURLlocalhost:4049/file.txtである。 req.uri.pathSegments.lastというコードはこの URI: file.txtからファイル名を抽出する。

CORSヘッダ

CORS(Cross-Origin Resource Sharing)の略語である。

セキュリティに問題があるので、ブラウザは要求に対しては組み込みアプリケーション(スクリプト)を作ったと同じサイトにあるものに限定している。逆に言えば要求をするコードは要求されるリソースと同じオリジン(ドメイン名、ポート番号、およびアプリケーション層プロトコル)からサービスされるものに限定される。つまり要求を行うコードを提供したと同じオリジン以外に属するリソースにアクセスできない。そうしておかないと、悪意のあるスクリプトがユーザの情報を別のサーバに送信させること(クロス・サイト・スクリプティング)を防げない。上記の例では、file.txtファイルはそれを使うアプリケーションと一緒に存在しなければならない。しかしCORSヘッダまたはJSONPを使えば、この制約を回避できる得る。

CORSW3Cが標準化(勧告として)しているものである。

CORSでは、クロス・サイト・アクセスを行うクライアント(ブラウザ)側とクロス・サイト・アクセスされるサーバー側のふるまいが規定されてい。クロス・サイト・アクセスされるサーバー側でアクセスを制御するルールを設定し、ブラウザとサーバー側でHTTPヘッダを使ってアクセス制御に関する情報をやりとりしながらサイトをまたいだアクセスをおこなう

サーバー側で設定するアクセスを制御するルール

  • クロスドメインアクセスを許可するWebページのオリジンサーバーのドメイン

  • 使用を許可するHTTPメソッド

  • 使用を許可するHTTPヘッダ

サーバ側(例えばnote_server.dartでは、この3つのルールを実装するとともに、これらをHTTP応答ヘッダに付加してブラウザのクライアントに返している:

void addCorsHeaders(HttpResponse response) {
  response.headers.add('Access-Control-Allow-Origin', '*');
  response.headers
      .add('Access-Control-Allow-Methods', 'POST, OPTIONS');
  response.headers.add('Access-Control-Allow-Headers',
      'Origin, X-Requested-With, Content-Type, Accept');
}

CRRSヘッダ対応のブラウザは通常次のどれかを実行する:

  • とにかく直接クロス・サイトのサーバのリソースにアクセスするHTTP要求を送信する

  • あらかじめそのサイトがクロス・サイトのアクセスが可能かどうかを知るためにHTTP要求を送信する



簡単な天気予報アプリケーション

ここではサービスへの登録が不要な天気予報のウェブ・サービスを使った簡単なアプリケーションのクライアントとサーバのコード例を示す。

天気予報ウェブ・サービスはグローバルにはOpenWeatherMapが良く使われている。このサービスのAPIは非常に豊富である。都市名、緯度経度、ZIPコードなどで指定した場所の現在の気象データ、5日間または16日間の予報、歴史データ、気象地図たちが得られる。このサイトはCORS対応しているのでブラウザだけでデータを取得できる。JavaScriptによるサンプル・コードは豊富に存在するので、これをDartに変換すれば良く、読者の練習にもなろう。

しかしここでは日本のLivedoorのお天気WebサービスLivedoor Weather Web Service / LWWS)を使ってみる。LWWSは現在全国142カ所の今日、明日、および明後日の天気予報、予想気温、および都道府県の天気概況情報を提供している。

JavaScriptDartなどのスクリプトを使ってこのウェブ・サービスをブラウザから直接アクセスするとCORSに対応していない為、エラーが発生し、ブラウザのJavascriptコンソールには次のように表示される:

XMLHttpRequest cannot load http://weather.livedoor.com/forecast/webservice/json/v1

?city=130010.

No 'Access-Control-Allow-Origin' header is present on the requested resource.

従ってここではサーバが仲介役となってLivedoorのお天気Webサービスにアクセスする。この方式はサーバ側からウェブ・サービスにアクセスしており、これはサーバ・サイドのマッシュアップ・アプリケーションのプログラム開発のベースにもなる。

このアプリを試してみる

このアプリケーションはgithub'weather_forecast_server'として公開してあるので、読者はダウンロードして実際に試してみると理解がはやくなる。

  1. IDE上でserver\pubspec.yamlを開き、その状態でGet dependenciesを実行する

  2. client\pubspec.yamlを開き、Get dependencies及び及びBuildコマンドを実行する

buildを実行した後は下図のような構成となっている:




  1. server\bin\server.dartを実行させるとサーバが立ち上がる

  2. 次にChromeDartiumFirefoxSafari及びIE-11などのブラウザからから"http://127.0.0.1:8080/weather" でこのサーバをアクセスすると、初期画面(即ち現時点で得られる東京の予報)が表示される。「都市を選択」の個所の選択メニューから知りたい都市を選択すれば、その都市の予報が表示される。

下図はこのアプリケーションのクライアントの初期画面を示す:




サービスの構成

ここではサーバは基本的に中継役(プロキシ)になり、JSONファイルの中身は関与させないサービスを考えることとする。こうしたのは、ブラウザ側でどのようにJSONファイルを処理するかのサンプルとする為である。

下図はサーバ側の構成とデータの流れである:




サーバ側では以下のファイルたちが置かれている:

  • /client/webに置かれたファイルたちクライアント側の為のファイルである。

  • /client/buildに置かれたファイルたちは以下の3つのファイルが含まれている。これらのファイルはクライアントのブラウザからの要求に基づきサーバがクライアントに渡す:

    • client.dart.jsclient.datJavaScriptコードにコンパイルしたファイルである

    • client.html/client/webに置かれた同じ名前のファイルのコピー

    • client.css/client/webに置かれた同じ名前のファイルのコピー

  • /server/binに置かれたファイルがサーバの実行ファイルたちである。server.dartがこのサービスの為のコードであり、残りはサーバ開発のテストに使われる。

  • /server/resoucesはクライアントに渡すリソースを収用する。初期はfaviconイメージのみであるが、クライアントと交信する中で天気のアイコンが蓄積されてゆく。

ブラウザ側での動作は次のようになる:

  1. ブラウザはhttp://localhost:8080/weatherでこのサーバを呼び出す

  2. サーバは/client/build/client.htmlを返す

  3. ブラウザはこのHTMLで指定されている/weather/client.dart.jsを要求し、サーバは/client/build/client.dart.jsを返す

  4. ブラウザは画面作成に必要な/weather/client.cssを要求し、サーバは/client/build/client.cssを返す

  5. 必要に応じブラウザはf/weather/avicon.icoを要求し、サーバは/resources/favicon.icoを返す

  6. 次にブラウザはデフォルト東京の天気情報を/weather/lwws?city=10040などを要求し、サーバはこれをLWWSのサイトからその都市に対するJSONデータを取得しこれをブラウザに返す

  7. ブラウザは画面表示に必要な天気のアイコン(例えば曇り、晴れ、晴れのち雨など)を/weather/lwws?image=1.gifなどと要求し、サーバは/resources/の中にキャッシュされている.gifファイルを返す

  8. もしそのファイルがキャッシュされていないときは、サーバはそれをLWWSから取り寄せ蓄積するとともに、ブラウザに返す

  9. ブラウザがある都市の天気予報を表示するのに必要なデータ次のようである:

/weather/lwws?city=10040など

LWWSからのその都市のJSONデータ

/weather/lwws?image=1.gifなど

LWWSからのその日の天気(雨のち晴れなど)のアイコンで、このサーバが保管していない場合はLWWSから取り寄せる保管する

このことから、サーバはファイル・サーバの機能と、LWWSのプロキシの機能で構成されている。

このような一連の流れは、サーバのログを見れば良くわかる。クライアントが要求したURIは赤で、それに対する応答は緑で、サーバがプロキシとしてLWWSに要求したものは橙で示してある。

14:29:33.876959: received a request from the client : method = GET, uri = /weather

14:29:33.877960: requested client side file :

14:29:33.879960: file handler : requested file : ../client/build/client.html

14:29:33.939964: sent response to the client for request : /weather with status = 200

14:29:33.950970: received a request from the client : method = GET, uri = /weather/client.dart.js

14:29:33.951964: requested client side file : /client.dart.js

14:29:33.951964: file handler : requested file : ../client/build/client.dart.js

14:29:33.954965: received a request from the client : method = GET, uri = /weather/client.css

14:29:33.954965: requested client side file : /client.css

14:29:33.954965: file handler : requested file : ../client/build/client.css

14:29:33.965964: sent response to the client for request : /weather/client.css with status = 200

14:29:33.978966: sent response to the client for request : /weather/client.dart.js with status = 200

14:29:34.060971: received a request from the client : method = GET, uri = /weather/lwws?city=130010

14:29:34.086972: received a request from the client : method = GET, uri = /favicon.ico

14:29:34.087971: file handler : requested file : resources/favicon.ico

14:29:34.123979: sent response to the client for request : /favicon.ico with status = 200

14:29:34.133979: LWWS interface : sent request for

http://weather.livedoor.com/forecast/webservice/json/v1?city=130010

14:29:34.200420: LWWS interface : received response :

http://weather.livedoor.com/forecast/webservice/json/v1?city=130010 with status = 200

14:29:34.208420: sent response to the client for request : /weather/lwws?city=130010 with status = 200

14:29:34.229423: received a request from the client : method = GET, uri = /weather/lwws?image=5.gif

14:29:34.230423: file handler : requested file : resources/5.gif

14:29:34.233422: received a request from the client : method = GET, uri = /weather/lwws?image=13.gif

14:29:34.233422: file handler : requested file : resources/13.gif

14:29:34.235424: received a request from the client : method = GET, uri = /weather/lwws?image=10.gif

14:29:34.240423: sent response to the client for request : /weather/lwws?image=5.gif with status = 200

14:29:34.241423: sent response to the client for request : /weather/lwws?image=13.gif with status = 200

14:29:34.247423: LWWS interface : sent request for http://weather.livedoor.com/img/icon/10.gif

14:29:34.257424: LWWS interface : received response : http://weather.livedoor.com/img/icon/10.gif with status = 200

14:29:34.258424: file handler : saved file : 10.gif

14:29:34.267425: file handler : requested file : resources/10.gif

14:29:34.270424: sent response to the client for request : /weather/lwws?image=10.gif with status = 200

  • クライアントが要求したデータの順序とサーバが返したデータの順序は必ずしも一致していないことに注意する。例えばクライアントがhtmlclient.dart.jsclient.cssLWWSJSONデータ、favicon.icoの順番に要求しているのに、サーバはhtmlclient.cssclient.dart.jsfavicon.icoLWWSJSONデータの順に返している。これはクライアントからの各要求に対する処理が総て非同期で行われている為である。

  • クライアントは送られてきたJSONデータをもとに本日、翌日、及び明後日の天気情報を表示するのに必要なイメージの5.gif13.gif、及び10.gifを要求しているが、そのうち最初の2つはサーバがファイルとして持っているので、直ちに返される。しかし残った10.gifはプロキシ経由でLWWSサーバから取り寄せ、それをサーバがファイルとして保存してからクライアントに返しているので少し時間がかかっている。しかしながらいずれは総てのイメージがサーバに蓄積されるので、それは余り気にする必要はなかろう。

クライアントのコード

クライアント即ちブラウザのコードはサーバと交信する必要がある。ブラウザで使われるAPIのライブラリであるdart:htmlJavaScriptに対応する巨大なものであるが、その中にはHTTPでサーバにアクセスする為のHttpRequestというクラスがある。dart:ioHttpRequestと間違えないよう注意が必要である。このクラスはこの章の終わりに翻訳してある。

ウェブ・サービスにアクセスする場合は次のようにHttpRequest.getStringというstaticメソッドが良く使われる:

var path = 'myData.json';
HttpRequest.getString(path)
    .then((String fileContents) {
      print(fileContents.length);
    })
    .catchError((Error error) {
      print(error.toString());
    });

これはURLを指定して文字列の応答を非同期で取得するものである。受信が終了したらthenで受ける。catchError Errorの型が帰ると記載されているが、現在はそうなっていないのでバグ報告をあげてある。

具体的にはclient.dartでは次のような記述となっている:

Future loadData() {
  log("Loading data");
  var url = "http://${host}?city=$cityCode";
  // call the web server asynchronously
  var completer = new Completer<String>();
  HttpRequest.getString(url, withCredentials: false).then((responseText) {
    logFlesh('JSON data received : \n$responseText');
    completer.complete(responseText);
  }).catchError((error) {
    log('requested data is not available : $error');
  });
  return completer.future;
}

ここで注意しなければならないのは、String型のオブジェクトを非同期で返す関数とするためにCompleterを用意し、最初にcompleter.futureを返し、String型のオブジェクトをcompleter.complete(responseText)でそのfutureを完了させるスタイルをとっていることである。一般に.thenで囲っていく(又はawaitで繋いでいく)ことでその最後の.then(又はawait)のなかでその処理を完了させるが、このように結果をFutureとして返す関数とするにはそのようなスタイルは使えない。

  • loadDataというFuture<String>を返す関数として記述されている。

  • HttpRequest.getStringFuture<String>を返すので、これをthenで受け、得られたテキスト返しfutureを完了させる

  • catchError200 OK応答以外の応答が帰ってきたときなどで発生するイベントを受ける。

更にクライアント側では受理したJsonオブジェクトをもとにリッチな画面を作るので、DOMが中心になるので、これのポイントを以下に解説する。

更にクライアント側では受理したJsonオブジェクトをもとにリッチな画面を作るので、DOMが中心になるので、これのポイントを以下に解説する。

クライアントのコードのポイント

client.dartに比べclient.htmlclient.cssはシンプルなものとなっている。その分DOMベースのdartコードがダイナミックな画面を生成する。JavaScriptDOMを理解している読者であれば、client.dartは比較的容易に追うことが可能であろう。このコードはクライアントのコードを書く場合のテンプレートとしても使えよう。但しモバイル向けのアプリではFlutterが使われ、その場合は別の記述でよりリッチな画面が書ける。

LWWS(Livedoor Weather Web Service)から(及びプロキシであるserver.dartから)返されてくるJSONテキストは、適当なJSONエディタでその構成を調べると次のようになっている:

object {8}

pinpointLocations [53]

link : http://weather.livedoor.com/area/forecast/130010

forecasts [2]

location {3}

publicTime : 2014-10-07T05:00:00+0900

copyright {4}

title : 東京都 東京 の天気

description {2}

  • このjsonObjectオブジェクトは8個の要素からなるMapである

  • pinpointLocationsはピンポイント情報を得るためのリンク先である(今回は使用しない)

  • linkは東京地方の詳細天気予報を知るためのURLである(今回は使用しない)

  • forecastsは今日、明日、明後日(もしあれば)の予報のListである

  • locationは都市、地域、都道府県の名前のMapである

  • publicTimeはこの予報の発表時刻のMapである

  • copyrightは著作権情報のMapである

  • titleは概況のタイトルのMapである

  • descriptionは概況と発表時刻のMapである

これをもとにJSON.decodeで得られるJsonObjectから必要なデータを取り出せばよい。

サーバのコード

サーバはサービスの構成の項で示したように、大きくはLWWSのプロキシの機能とファイル・サーバの機能が中心となっている。とりわけプロキシの機能(ここではLWWSインターフェイスとも記してある)は今回初めて登場するので、これを中心に説明する。



LWWSへのサーバからのアクセス

Dart:ioにはHTTPでサーバと交信する用途の為にHttpClientという抽象クラス(日本語に訳したものはこの章の終わりにある)が用意されている。つまりサーバが対象とするウェブ・サービスのクライアントとなる。サーバウェブ・サービスにたいしHttpClientRequestを送信し、その応答をHttpClientResponseとして受理する。

HttpClientは別のHTTPサーバに対しHttpClientRequestを送信し、戻ってくる HttpClientResponseを受信するための一連のメソッドたちを含む。例えば、GETおよびPOST要求に対しget, getUrl, post,およびpostUrlメソッドが使える。

一番シンプルなコードは次のような記述になる:

HttpClient client = new HttpClient();

client.getUrl(Uri.parse("http://www.example.com/"))

.then((HttpClientRequest request) {

// 必要ならヘッダたちをセット...

// 必要なら要求オブジェクトに対しデータを書き込む...

// その後HttpClientRequestcloseを呼ぶ(Future<HttpClientResponse>が返される)

...

return request.close();

})

.then((HttpClientResponse response) {

// Process the response.

...

});

  • getUrlメソッドはサーバにGET要求をするためのオブジェクトを生成し、サーバとの接続を行う

  • これが完了したら、HTTP要求をそのサーバに送るためのHttpClienRequestのオブジェクトが渡されるので、そのオブジェクトに対しヘッダやボディ部をセットする

  • HttpClientRequestcloseメソッドはHTTP要求をサーバに送信する(ヘッダ部は先に送信されている場合がある)。デフォルトではボディ部はGZIP圧縮して送信される

  • その後サーバからの応答を受信したらFuture<HttpClientResponse>が返されるので、thenで受けて受信した応答の処理を行う

サーバからLWWSのような認可や登録を必要としないウェブ・サービスをアクセスする一般的なコードは次のようになる。このコードはserver/bin/lwws_access_test.dartとしてこのアプリケーションのなかに同梱してあるので、これを直接自分のIDEから実行させて試して頂きたい。

/**
 * Sample to access a web service server (no credentials)
 * Gets Tokyo area weather forecast from LWWS as jsonObject
 */
import 'dart:io';
import 'dart:convert';
const host = "weather.livedoor.com/forecast/webservice/json/v1";
const cityCode = 130010; // Tokyo

main() {
  HttpClient client = new HttpClient();
  var bodyStr = '';
  client
      .getUrl(Uri.parse("http://${host}?city=${cityCode}"))
      .then((HttpClientRequest request) {
    return request.close();
  }).then((HttpClientResponse response) {
    // Process the response.
    response.transform(utf8.decoder).listen((bodyChunk) {
      bodyStr = bodyStr + bodyChunk;
    }, onDone: () {
      // handle data
      print('***headers***\n${response.headers}');
      var jsonObj = json.decode(bodyStr);
      print('***bodyString***\n$bodyStr');
      print('***jsonObject***\n$jsonObj');
      print('**forecasts***\n${jsonObj["forecasts"]}');
      print('***description***\n${jsonObj["description"]}');
    });
  });
}

幾つかのポイントを示すと:

  • サーバからの応答ヘッダにはcontent-type: application/json; charset=utf-8となっていて、UTF-8でエンコードされているので、transform(UTF8.decoder)でデコードしてとりだす。

  • またボディ部はtransfer-encoding: chunkedとヘッダに記されているように、チャンクで送信されてくるので、これを文字列として連結する必要がある。

  • 最終的にデータはonDoneのコールバックで得られる。

  • JSON.decodeメソッドはJsonオブジェクトと呼ばれるMapで得られる。

server.dartの場合は_getBytesという関数で実現されている。

この関数もFutureを返し、非同期で進行する。この関数ではイメージ・ファイルも受け取るの為、バイト列としてそのままクライアントに転送しなければならないので、UTF-8変換することなくFuture<List<int>>の形で結果をわたす。また200OK)以外の応答が帰ってきたときは、404NOT FOUND)応答をクライアントに返している。

Future<List<int>> _getBytes(Uri uri, HttpRequest request) {
  List<int> bodyBytes = [];
  var completer = Completer<List<int>>();
  try {
    HttpClient client = new HttpClient();
    client.getUrl(uri).then((req) {
      if (LOG_REQUESTS) log('LWWS interface : sent request for $uri');
      req.close().then((res) {
        // Process the response.
        res.listen((bodyChunk) {
          bodyBytes.addAll(bodyChunk);
        }, onDone: () {
          // handle data
          if (res.statusCode == HttpStatus.ok) {
            if (LOG_REQUESTS)
              log('LWWS interface : received response : $uri with status = ${res.statusCode}');
            completer.complete(bodyBytes);
          } else
            notFoundHandler.onRequest(request); // not a status OK (200)
        });
      });
    });
  } catch (err, st) {
    log('LWWS Handler getLwws error : ${err.toString()}\n${st}');
  }
  return completer.future;
}



関連APIの和訳

Dart:html.HttpRequestクラス

オリジナル・ドキュメント:https://api.dartlang.org/stable/2.2.0/dart-html/HttpRequest-class.html

HttpRequest class

あるURLからデータを取得する為のクライアント側のXHR(XMLHttpRequest)要求で、以前はXMLHttpRequestとして知られていたもの。

HttpRequestHTTPおよびFTPプロトコルでデータを取得するのに使え、AJAXスタイルのページの更新に有用である。

JSONフォーマットされたファイルのようなテキスト・ファイルの中身を取得する最もシンプルな方法は、getStringメソッドを使うことである。例えば、以下のコードはJSONファイルの中身を取得しその長さをプリントする:

var path = 'myData.json';

HttpRequest.getString(path)

.then((String fileContents) {

print(fileContents.length);

})

.catchError((Error error) {

print(error.toString());

});

他のサーバからのデータの取得について。


セキュリティの理由から、ブラウザは組み込みアプリケーションからの要求には制限を課している。このクラスのデフォルトの振る舞いでは、この要求をするコードは要求されているリソースと同じオリジン(ドメイン名、ポート番号、およびアプリケーション層プロトコル)から渡されねばならない。上記の例では、myData.jsonファイルはこれを使うアプリケーションと共に置かれて居なければならない。この制約はCORSヘッダまたはJSONPを使って回避し得る。

実装

Object→EventTarget→HttpRequestEventTarget→HttpRequest

コンストラクタ

HttpRequest()

どのタイプの要求(GETPOST等)にも使える汎用コンストラクタ。

この呼び出しはopenと一緒にして使われる:

var request = new HttpRequest();

request.open('GET', 'http://dartlang.org');

request.onLoad.listen((event) => print(

'Request complete ${event.target.reponseText}'));

request.send();

これは以下と等価であるが、ややくどい記述である:

HttpRequest.getString('http://dartlang.org').then(

(result) => print('Request complete: $result'));


メソッド

void abort()

現行の要求を止める。

readyState HEADERS_RECIEVED または LOADINGの状態である時のみこの要求は停止できる。このメソッドが送信中でないときは、このメソッドは効果を持たない。

String getAllResponseHeaders()

(安定していない)

ある要求からの総ての応答ヘッダをとりだす。

ヘッダを受信していないときはnullを返す。マルチパート要求の場合は、 getAllResponseHeadersはその要求の現行のパートに対する応答ヘッダを返す。

一般的な応答ヘッダのリストに対すしてはHTTP応答ヘッダを見ること。

String getResponseHeader(String header)

(安定していない)

指定したヘッダをもった応答を返し、見つからないときはnullを返す。

一般的な応答ヘッダのリストに対すしてはHTTP応答ヘッダを見ること。

void open(String method, String url, {bool async, String user, String password})

この要求をするときはurlmethodを指定する。

デフォルトでは、この要求は非同期で行われ、パスワード認証情報を持たない。もしasyncfalseなら、この要求は同期的に送信される。

現行のアクティブな状態の要求にopenを再度呼ぶことはabortを呼ぶことと等価である。

注意:ほとんどのシンプルなHTTP要求はgetStringrequestrequestCrossOrigin、またはpostFormData のメソッドを使って達成される。このopenメソッドを使うのは、細かなコントロールが必要なより複雑なHTTP要求にの場合のみに意図されている。

void overrideMimeType(String override)

その要求にとって必要な特定のMIMEタイプ(例えばtext/xml のような)を指定する。

この値はその要求が送信される前にセットされねばならない。一般的な応答ヘッダのリストに対すしてはHTTP応答ヘッダを見ること。

@SupportedBrowser(SupportedBrowser.CHROME), @SupportedBrowser(SupportedBrowser.FIREFOX), @SupportedBrowser(SupportedBrowser.SAFARI)

void send([data])

与えられたdataでその要求を送信する。

注意:殆どのシンプルなHTTP要求はgetStringrequestrequestCrossOrigin、またはpostFormData のメソッドを使って達成される。このopenメソッドを使うのは、細かなコントロールが必要なより複雑なHTTP要求にの場合のみに意図されている。

他のリソース: XMLHttpRequest.send from MDN.

void setRequestHeader(String header, String value)

あるHTTP要求ヘッダの値をセットする。

このメソッドはこの要求がopenした後で且つ該要求が送信される前に呼ばれねばならない。

同じヘッダに対し複数回呼ぶとそれらは単一ヘッダとしてまとめられる。

他のリソース: XMLHttpRequest.setRequestHeader method from MDN.

         setRequestHeader() method-from W3C.

void addEventListener(String type, EventListener listener, [bool useCapture])

EventTargetから継承

bool dispatchEvent(Event event)

EventTargetから継承

noSuchMethod(Invocation invocation)

Interceptorから継承


ユーザがあるオブジェクトに対し存在しないメソッドを呼んだときにnoSuchMethodが呼び出される。この呼び出しの名前と引数たちは Invocationのなかの noSuchMethodに渡される。noSuchMethod が値を返す時は、その値がオリジナルの呼び出しの結果となる。


noSuchMethodのデフォルトの振る舞いは NoSuchMethodErrorをスローすることである。

void removeEventListener(String type, EventListener listener, [bool useCapture])

EventTargetから継承

String toString()

EventTargetから継承


このオブジェクトの文字列表現を返す。

演算子

bool ==(other)

EventTargetから継承

対等性演算子。

総てのObjectにたいする振る舞いはthisotherが同じオブジェクトであるときに限りtrueを返す。

あるクラス上で別の対等性の関係を規定するときはこのメソッドをオーバライドする。オーバライドするほうのメソッドはそれでも対等性の関係を保持しなければならない。即ち、以下のごとくあらねばならない:

Total: それは総ての引数に対しブール値を返さねばならない。決してスローしたりnullを返してはいけない。

  • Reflexive: 総てのオブジェクトoに対し、o == o trueでなければならない。

  • Symmetric: 総てのオブジェクトo1o2に対し、 o1 == o2o2 == o1はともにtrueでもともにfalseでもあってはいけない。

  • Transitive: 総てのオブジェクトo1, o2, 及び o3にたいし、もし o1 == o2 及び o2 == o3 trueなら、o1 == o3 trueでなければならない。a

このメソッドは近いとともに一貫していなければならないので、2つのオブジェクトは時間とともに変化してはいけないか、あるいは少なくとも一つのオブジェクトに変化を与えられたときにのみ変化する。

あるサブクラスがこの演算子をオーバライドするときは、一貫性を保つためにはhashCodeもオーバライドしなければならない。

Staticメソッド

static Future<String> getString(String url, {bool withCredentials, Function void onProgress(ProgressEvent e)})

指定したurlむけのGET要求を生成する。

この要求が成功するにはサーバの応答は text/ mimeタイプでなければならない。

これはrequestと似ているがテキストの中身を返すHTTP GET 要求に特化している。

クエリ・パラメタを付加するには、urlのあとに?を付けそのあとにそれらを追加する。その際各キーと値は=で結び付け、各キーと値のペアを&で分離する。

var name = Uri.encodeQueryComponent('John');

var id = Uri.encodeQueryComponent('42');

HttpRequest.getString('users.json?name=$name&id=$id')

.then((HttpRequest resp) {

// Do something with the response.

});

See also: request

static Future<HttpRequest> postFormData(String url, Map<String, String> data, {bool withCredentials, String responseType, Map<String, String> requestHeaders, Function void onProgress(ProgressEvent e)})

指定したdataformデータとして付したPOST要求をサーバにする。

これはほぼgetStringPOST等価なメソッドである。このメソッドはより広いブラウザ・サポートを持つ FormDataオブジェクトを送信することと等価であるがStringの値に限定されている。

もしdataが与えられたら、そのキー/値のペアは encodeQueryComponentURIエンコードされ、HTTPクエリ文字列に変換される。

指定されていない限り、このメソッドは以下のヘッダを追加する:

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

以下はこのメソッドの使用例である:

var data = { 'firstName' : 'John', 'lastName' : 'Doe' };

HttpRequest.postFormData('/send', data).then((HttpRequest resp) {

// Do something with the response.

});

See also: request

static Future<HttpRequest> request(String url, {String method, bool withCredentials, String responseType, String mimeType, Map<String, String> requestHeaders, sendData, Function void onProgress(ProgressEvent e)})

指定したurlに対する要求を生成し送信する。

デフォルトではrequestHTTP GET要求を行うが、methodパラメタを指定することで他のメソッド(POST, PUT, DELETE, etc)も使用可能である。(POST要求だけの場合のpostFormDataも参照のこと)返されるFutureはその応答が得られたら完了する。

指定されていれば、sendData ByteBuffer, Blob, Document, String,またはFormDataの形式のデータをHttpRequestに載せて送信する。

指定されていれば、responseType はその要求に対する欲しい応答書式をセットする。デフォルトではそれはStringであるが、'arraybuffer', 'blob', 'document', 'json', または'text'が指定できる。更なる情報は responseType を参照のこと。

withCredentialsパラメタは(既に)クッキーのようなクレデンシャルがヘッダにセットされているか、その要求にauthorization ヘッダを指定するかしなければならない。クレデンシャルを使うときの注意点:

  • クレデンシャルの使用はクロス・オリジンの要求に対しのみ有用である。

  • urlAccess-Control-Allow-Originヘッダはワイルド・カード(*)を含むことはできない。

  • urlAccess-Control-Allow-Credentialsヘッダはtrueにセットされていなければならない。

  • Access-Control-Allow-Credentialsヘッダがまだtrueにセットされていないときは、 getAllRequestHeadersを呼んだときに総ての応答ヘッダたちのみが返される。

これは上記の getStringサンプルと等価なものである:

var name = Uri.encodeQueryComponent('John');

var id = Uri.encodeQueryComponent('42');

HttpRequest.request('users.json?name=$name&id=$id')

.then((HttpRequest resp) {

// Do something with the response.

});

以下はFormDataでフォーム全部をサブミットする例である:

Here's an example of submitting an entire form with FormData.


var myForm = querySelector('form#myForm');

var data = new FormData(myForm);

HttpRequest.request('/submit', method: 'POST', sendData: data)

.then((HttpRequest resp) {

// Do something with the response.

});

file:// URIsへの要求はそのマニフェストでしかるべき許可をしたChrome拡張でのみサポートされる。file:// URIsへの要求はまた決して失敗は起きず、例えそのファイルが見つからないときでも常に成功で完了する。

See also: authorization headers.

static Future<String> requestCrossOrigin(String url, {String method, String sendData})

指定したURLにクロス・オリジン要求を行う。

このAPIIE9で動作するサブセットである。もしIE)のクロス・オリジン・サポートが必要ないときは、その代りrequestを使うべきである。

属性

Stream<ProgressEvent> get onReadyStateChange

このHttpRequestEventTargetで扱われたreadystatechange イベントのストリーム。

イベント・リスナたちはこのHttpRequestオブジェクトの readyStateの値が変わった時毎に通知される。

final int readyState

この要求の現在の状態を示すインディケータ:

Value

State

意味

0

unsent

open()がまだ呼ばれていない

1

opened

send()が未だ呼ばれていない

2

headers received

sent()が呼ばれている:応答ヘッダとstatusが取得できる

3

loading

responseTextが何らかの値を持っている

4

done

要求が完了している


get response

その要求からの応答として受信したデータ。

データはString, ByteBuffer, Document, Blob, or json (also a String)であり得る。nullは要求が失敗したことを示す。

@SupportedBrowser(SupportedBrowser.CHROME), @SupportedBrowser(SupportedBrowser.FIREFOX), @SupportedBrowser(SupportedBrowser.IE, '10'),

@SupportedBrowser(SupportedBrowser.SAFARI), read-only

Map<String, String> get responseHeaders

総ての応答ヘッダたちをキー/値のMapとして返す。

同じヘッダ・キーに複数の値があるときは一つにまとめられ、カンマとスペースで区切られる。

See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method

final String responseText

Stringの形式の応答で、空のStringの時は失敗したことを示す。

String responseType

サーバに対し欲しい応答のフォーマットを告げるためのString

デフォルトはStringだが、それ以外に'arraybuffer', 'blob', 'document', 'json', 'text'のどれかを選択できる。

同期要求を行っている際に responseTypeがセットされているときに一部のブラウザでは NSERRORDOMINVALIDACCESS_ERRをスローする。

See also: MDN responseType

final String responseUrl

@JSName('responseURL'), final

final Document responseXml

要求に対する応答で、失敗したときはnullである。

その応答はresponseType = 'document'でその要求が同期でないときに text/xmlのストリームとして処理される。

@JSName('responseXML'), final

final int status

その要求に対する応答の結果コード(200404等)

See also: Http Status Codes

final String statusText

応答ステータスの文字列(such as \"200 OK\")

See also: Http Status Codes

int timeout

ある要求が自動的に終了するまでの時間長。timeを超過したらTimeoutEvent で通知される。time0にセットすると、この要求はタイムアウトを起こさない。

Other resources

XMLHttpRequest.timeout from MDN.

The timeout attribute from W3C

bool withCredentials

クロス・サイトの要求がクッキーのようなクレデンシャルを使うかまたは authorizationヘッダを使うかするときはtrueとする。それ以外はfalseである。

同じサイトへの要求ではこの値は無視される。

int get hashCode

EventTargetから継承

このオブジェクトのハッシュ・コードを取得する。

HttpResponseUpload upload

その要求の進捗状況を追跡するためにリスナたちを保持できるEventTarget。ファイアされるイベントは HttpRequestUploadEventsのメンバとなる

@Unstable(), final

Events get on

EventTargetから継承

これはイベント・ストリームに対する使いやすいアクセサであって、明示的なアクセサがないときにのみ使われるべきである。

Stream<ProgressEvent> get onAbort

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたabortイベントのストリーム。

Stream<ProgressEvent> get onError

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたerrorイベントのストリーム。

Stream<ProgressEvent> get onLoad

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたloadイベントのストリーム。

Stream<ProgressEvent> get onLoadEnd

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたloadendイベントのストリーム。

@SupportedBrowser(SupportedBrowser.CHROME), @SupportedBrowser(SupportedBrowser.FIREFOX),

@SupportedBrowser(SupportedBrowser.IE, '10'), @SupportedBrowser(SupportedBrowser.SAFARI),

Stream<ProgressEvent> get onLoadStart

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたloadstartイベントのストリーム。

Stream<ProgressEvent> get onProgress

HttpRequestEventTargetから継承

このHttpRequestEventTargetで扱われたprogressイベントのストリーム。

@SupportedBrowser(SupportedBrowser.CHROME), @SupportedBrowser(SupportedBrowser.FIREFOX),

@SupportedBrowser(SupportedBrowser.IE, '10'), @SupportedBrowser(SupportedBrowser.SAFARI)

final HttpRequestUpload upload

該要求の進捗状況を追跡するためのリスナを保持できるEventTarget。ファイアするイベントは HttpRequestUploadEventsのメンバとなる。

@Unstable(), final

Stream<ProgressEvent> get onTimeout

HttpRequestEventTargetから継承


このHttpRequestEventTargetで扱われたtimeoutイベントのストリーム。

Type get runtimeType

Interceptorから継承


このオブジェクトの実行時の型を表現する。

Comment inherited from Object

Static属性

static bool get supportsCrossOrigin

現行のプラットホームがクロス・オリジン要求をするのに対応しているかどうかをチェックする。

注意:たとえクロス・オリジン要求に対応していても、相手のサーバがCORS要求に対応していなければその要求は失敗する。

static bool get supportsLoadEndEvent

現行のプラットホームが LoadEndイベントに対応しているかどうかをチェックする。

static bool get supportsOverrideMimeType

現行のプラットホームが overrideMimeType メソッドに対応しているかどうかをチェックする。

static bool get supportsProgressEvent

現行のプラットホームがProgressイベントに対応しているかどうかをチェックする。



Dart:io.HttpClientクラス

オリジナル・ドキュメント:https://api.dartlang.org/stable/2.2.0/dart-io/HttpClient-class.html

HttpClient class


HTTPプロトコルを使ってウェブ・ページのようなコンテントをあるサーバから受信するクライアント。

HttpClientはあるHTTPサーバに対し HttpClientRequestを送信し、戻ってくる HttpClientResponseを受信するための一連のメソッドたちを含む。例えば、GETおよびPOST要求に対しget, getUrl, post,およびpostUrlメソッドが使える。


シンプルなGET要求をする例:

getUrl 要求は2つのFutureがトリガとなる2段階のプロセスである。HttpClientRequestで最初のFutureが完了したときは、その背後のネットワーク接続が確立したがデータはまだ送信されていない状態である。最初のFutureのコールバック関数の中で、HTTPヘッダたちとボディ部がその要求上でセットできる。その要求オブジェクトに最初に何かを書き込んだとき、あるいはcloseを呼んだときにその要求はサーバに送信される。

そのサーバからHTTP応答が受信したときは、closeで返された第2FutureHttpClientResponseオブジェクトで完了する。このオブジェクトが該応答のヘッダたちおよびボディへのアクセスを提供する。このボディは HttpClientResponseで実装されているストリームによって取得できる。もしボディ部が存在していれば、それは読み出しされねばならず、そうでないとリソースのリークをもたらす。ボディを使わないときはHttpClientResponse.drainを使うことを検討すること。

HttpClient client = new HttpClient();

client.getUrl(Uri.parse("http://www.example.com/"))

.then((HttpClientRequest request) {

// Optionally set up headers...

// Optionally write to the request object...

// Then call close.

...

return request.close();

})

.then((HttpClientResponse response) {

// Process the response.

...

});

HttpClientRequestFuturegetUrlおよびopenのようなメソッドによって生成される。


ヘッダ

総てのHttpClientの要求はデフォルトでは次のヘッダ行が付加される:

Accept-Encoding: gzip

これにより該HTTPサーバはそのボディにたいし可能ならgzip圧縮をかける。こうしてほしくないときは、 Accept-Encodingヘッダをなにか別のものに変更する。応答のgzip圧縮をオフにするときは、このヘッダをオフにする:

request.headers.removeAll(HttpHeaders.ACCEPT_ENCODING)


HttpClientを閉じる

HttpClientは出来うる限り複数の要求の為に再利用する為の永続した接続およびキャッシュ網接続に対応している。このことはある要求が完了した後もある時間ネットワーク接続を保持され得ることを意味する。強制的にこの接続をシャットダウンし、アイドル状態のネットワーク接続を閉じるには、HttpClient.close を使う。


プロキシのオンとオフ

デフォルトではHttpClient はその環境で使えるプロキシ構成を使用する、findProxyFromEnvironmentメソッドを参照のこと。プロキシ使用をオフにするにはfindProxy属性をnullにする:

HttpClient client = new HttpClient();
client.findProxy = null;



		

継承

Object


コンストラクタ

HttpClient()


メソッド

void addCredentials(Uri url, String realm, HttpClientCredentials credentials)

HTTP要求を認可するために使われるクレデンシャル(証明書)たちを付加する。

void addProxyCredentials(String host, int port, String realm, HttpClientCredentials credentials)

HTTPプロキシたちを認可するために使われるクレデンシャルたちを付加する。

void close({bool force: false})

HTTPクライアントを閉じる。forcefalseのとき(デフォルト)は総てのアクティブな接続が完了するまでこのHttpClientは活きつづける。forcetrueのときは、アクティブな接続は閉じられ、総てのリソースが解放される。これらの閉じた接続たちは該接続がシャットダウンしたことを示す為のonErrorコールバックを受ける。双方の場合においてシャットダウンを呼んだ後で新規の接続を確立しようとすると例外が生起する。

Future<HttpClientRequest> delete(String host, int port, String path)

HTTPDELETEメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> deleteUrl(Uri url)

HTTPDELETEメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

Future<HttpClientRequest> get(String host, int port, String path)

HTTPGETメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> getUrl(Uri url)

HTTPGETメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

Future<HttpClientRequest> head(String host, int port, String path)

HTTPHEADメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> headUrl(Uri url)

HTTPHEADメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

Future<HttpClientRequest> open(String method, String host, int port, String path)

HTTP接続を行う。

使用するHTTPメソッドはmethodで指定され、このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

要求の為のHostヘッダは host:portという値でセットされる。これはこの要求が送信される前であればHttpClientRequestインターフェイスを介してオーバライドされ得る。

注意:もしhostIPアドレスの時は、これはHostヘッダにセットされたままとなる。

HTTPトランザクション中のイベントたちのシーケンス、およびfutureたちによって返されるオブジェクトたちに関する更なる情報は、HttpClientクラスの全体ドキュメンテーションを参照のこと。

Future<HttpClientRequest> openUrl(String method, Uri url)

HTTP接続を行う。

HTTPメソッドはmethodで指定し、URLurlで指定する。

要求の為のHostヘッダは host:portという値でセットされる。これはこの要求が送信される前であればHttpClientRequestインターフェイスを介してオーバライドされ得る。

注意:もしhostIPアドレスの時は、これはHostヘッダにセットされたままとなる。

HTTPトランザクション中のイベントたちのシーケンス、およびfutureたちによって返されるオブジェクトたちに関する更なる情報は、HttpClientクラスの全体ドキュメンテーションを参照のこと。

Future<HttpClientRequest> patch(String host, int port, String path)

HTTPPATCHメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> patchUrl(Uri url)

HTTPPATCHメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

Future<HttpClientRequest> post(String host, int port, String path)

HTTPPOSTメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> postUrl(Uri url)

HTTPPOSTメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

Future<HttpClientRequest> put(String host, int port, String path)

HTTPPUTメソッドを使ってHTTP接続を行う。

このサーバはhostportを使って指定され、パス(フラグメントとクエリが付加されている場合もあり)はpathを使って指定される。

詳細はopenを参照のこと。

Future<HttpClientRequest> putUrl(Uri url)

HTTPPUTメソッドを使ってHTTP接続を行う。

使用するサーバのURLurlで指定される。

詳細はopenUrl を参照のこと。

staticメソッド

static String findProxyFromEnvironment(Uri url, {Map<String, String> environment})

environment変数で指定されたプロキシ構成から、HTTP接続に使われるプロキシ・サーバを得るための関数。

以下のenvironment変数たちが調べられる:

http_proxy
https_proxy
no_proxy
HTTP_PROXY
HTTPS_PROXY
NO_PROXY

http_proxyHTTP_PROXYhttp:// urlsに使われるプロキシ・サーバを指定する。hostname:portを使う。portがないときはデフォルトとして1080が使われる。双方ともにセットされていると小文字のほうが優先する。

https_proxyHTTPS_PROXYhttp:// urlsに使われるプロキシ・サーバを指定する。hostname:portを使う。portがないときはデフォルトとして1080が使われる。双方ともにセットされていると小文字のほうが優先する。

no_proxyNO_PROXYはプロキシ・サーバに使わないhostnameたちをカンマで区切ったリストを指定する。例えば"localhost,127.0.0.1"という値は"localhost""127.0.0.1"の双方への要求にはプロキシを使わなくする。双方ともにセットされていると小文字のほうが優先する。

このプロキシ解決手段を活かすには、この関数を HttpClient上でfindProxyに代入する。

HttpClient client = new HttpClient();
client.findProxy = HttpClient.findProxyFromEnvironment;

システム環境を使いたくないときは、この関数をラップして異なったものを使うことができる:

HttpClient client = new HttpClient();
client.findProxy = (url) {
  return HttpClient.findProxyFromEnvironment(
      url, {"http_proxy": ..., "no_proxy": ...});
}

もしプロキシが認可を必要とする場合は、 usernamepasswordも設定することができる。usernamepasswordも含めるときは username:password@hostname:portという書式を使う。別のやりかたとして、 addProxyCredentialsというAPIが認可を要求とするプロキシにクレデンシャルをセットするのに使える。

属性

set authenticate(Function Future<bool> f(Uri url, String scheme, String realm))

そのサイトが認可を要求している場合に呼ばれる関数をセットする。要求されるURLとサーバからのセキュリティ・レルムはurlrealm変数で渡される。

この関数はFutureを返し、認証が終わった時に完了する。クレデンシャルたちが与えられない場合はFuturefalseで終了する。クレデンシャルたちが指定されたときは、この関数はtruevalueFutureが完了される前に addCredentialsを使ってこれらを付加しなければならない。

Futuretrueで完了したときは、この要求は更新されたクレデンシャルたちを使って再度試行される。そうでないときは、応答処理は通常通り継続する。

set authenticateProxy(Function Future<bool> f(String host, int port, String scheme, String realm))

プロキシが認可を要求している場合に呼ばれる関数をセットする。使用しているプロキシに関する情報とその認証の為のセキュリティ・レルムはhostportrealm変数で渡される。

この関数はFutureを返し、認証が終わった時に完了する。クレデンシャルたちが与えられない場合はFuturefalseで終了する。クレデンシャルたちが得られるときは、この関数はtruevalueFutureが完了される前に addProxyCredentialsを使ってこれらを付加しなければならない。

Futuretrueで完了したときは、この要求は更新されたクレデンシャルたちを使って再度試行される。そうでないときは、応答処理は通常通り継続する。

bool autoUncompress

この応答のボディ部が自動的に圧縮されるかどうかを取得および設定する。

HTTP応答のボディ部は圧縮し得る。殆どの場合解凍したボディを渡すことが最も都合がよい。従ってデフォルトの振る舞いはボディ部は解凍されることになっている。しかしながらある状況(例えば透過プロキシの実装)では解凍しないストリームを維持することが求められる。

注意:応答からのヘッダは加工されることはない。このことは、自動回答がオンになっていても Content-Lengthヘッダはオリジナルの圧縮されたボディの長さを反映していることを意味する。同様に、 Content-Encodingヘッダは圧縮を意味するオリジナルの値を保持している。

注意:自動解凍はContent-Encodingヘッダの値がgzipであるときのみ行われる。

この値が変更された後は、このクライアントが生成する総ての応答にその値が適用される。

自動解凍をオフにするときは、falseをセットする。

デフォルトはtrueである。

set badCertificateCallback(Function bool callback(X509Certificate cert, String host, int port))

我々の信頼されるルート認証たちのどれを使っても認可が得られないサーバ認証でセキュアな接続を受け付けるかどうかを判断するコールバック関数をセットする。

セキュアなHTTP要求がこのHttpClientを使ってでき、そのサーバが認可できなかったs-場認証を返したときは X509認証オブジェクトおよびそのサーバのhostnameportでこのコールバックが非同期で呼ばれる。badCertificateCallback= の値がfalseのときは、その合わない認証は、あたかもそのコールバックがfalseを返したごとく排除される。

もしこのコールバックがtrueを返す時は、そのセキュアな接続は受け付けられ、その要求をしている呼び出しから返されたFuture<HttpClientRequest> は有効なHttpRequestオブジェクトで完了する。このコールバックがfalseを返す時は、Future<HttpClientRequest>は例外で完了する。

試みている接続上で認証できず(bad certificate)が受信されているとき、このライブラリはたとえそれ以来 badCertificateCallbackの値が変わっていたとしても、その要求がなされた時点での badCertificateCallbackの値である関数を呼び出す。

set findProxy(Function String f(Uri url))

指定したurlに対しHTTP接続を開くのに使われるプロキシ・サーバを解決するのに使われる関数をセットする。この関数がセットされていないときは、常に直接接続が使われる。

fが返すStringはブラウザPAC (proxy auto-config)スクリプトで使われる書式でなければならない。即ち以下のどれかでなければならない:

直接接続使用の為の

"DIRECT"

またはポートport上のプロキシ・サーバhostを使うための

"PROXY host:port"

設定はセミコロンで区切られた幾つかの要素を含むことがあり得る。例えば:

"PROXY host:port; PROXY host2:port2; DIRECT"

このクラスのstatic関数のfindProxyFromEnvironmentが環境変数に基づくプロキシ・サーバ解決を実装するのに使える。

Duration idleTimeout

非アクティブな永続した(keep-alive) 接続のアイドルのタイムアウトの取得および設定。デフォルト値は15秒である。

int maxConnectionsPerHost

単一のホストに対するライブな接続の最大数の取得と設定。

この数を増やすと性能を下げ不必要なシステム・リソースの消費をもたらし得る。

これを殺す時はnullをセットする。

デフォルトはnullである。

String userAgent

この HttpClientが生成した総ての要求におけるデフォルトのUser-Agentヘッダの値の取得と設定。デフォルトの値はDart/<version> (dart:io)である。

userAgent nullに設定されているときは、各要求にはデフォルトの User-Agentヘッダは付加されない。

static属性

static const int DEFAULT_HTTPS_PORT


static const int DEFAULT_HTTP_PORT




Dart:io.HttpClientRequestクラス

オリジナル・ドキュメント:https://api.dartlang.org/stable/2.2.0/dart-io/HttpClientRequest-class.html

HttpClientRequest class


あるクライアント接続の為のHTTP要求。

要求を準備するには、このクラスで用意されている headersプロパティを使ってヘッダたちをセットし、この要求のボディにデータを書き込む。 HttpClientRequestIOSinkである。このHTTP要求のボディに書き込むにはwriteCharCode()のようなIOSinkのメソッドたちのひとつを使用する。IOSinkのメソッドたちのどれかが初めて使われると要求ヘッダが送信される。それが送信された後でヘッダを変えるようなメソッドが呼ばれると例外がスローされる。

このIOSinkを介して文字列データを書き込むときは使われるエンコーディングは"Content-Type"ヘッダの"charset"パラメタで決まる。

HttpClientRequest request = …

request.headers.contentType = new ContentType("application", "json", charset: "utf-8");

request.write(...); // Strings written will be UTF-8 encoded.

charsetが指定されていないときは、デフォルトのがISO-8859-1 (Latin 1)使われる。

HttpClientRequest request = …
request.headers.add(HttpHeaders.contentTypeHeader, "text/plain");
request.write(...);  // Strings written will be ISO-8859-1 encoded.

対応していないエンコーディングを使い、stringパラメタを使うwrite()メソッドが使われていると例外がスローされる。

継承

IOSink

コンストラクタ

HttpClient()HttpClientRequest()

メソッド

close() → Future<HttpClientResponse>

入力の為のこの要求を閉じる。完了の値を返す。

override

add(List<int> data) → void

エンコーディングを無視してターゲットのコンシューマにバイト・データを加える。

inherited

addError(Object error, [ StackTrace stackTrace ]) → void

ターゲットのコンシューマにエラー・イベントとしてerrorを渡す。

inherited

addStream(Stream<List<int>> stream) → Future

指定したストリームの全要素をこれに加える。

inherited

flush() → Future

総てのバッファされていたデータがStreamConsumerで受け付けられたら完了するFutureを返す。

inherited

noSuchMethod(Invocation invocation) → dynamic

存在しないメソッドまたはプロパティがアクセスされたときに呼び出される。

inherited

toString() → String

このオブジェクトの文字列表現を返す。

inherited

write(Object obj) → void

Object.toStringを使ってobjStringに変換し、その結果のエンコーディングしたものをターゲットのコンシューマに加える。

inherited

writeAll(Iterable objects, [ String separator = "" ]) → void

与えられたオブジェクト上で繰り返し、それらを順番に書き込む。

inherited

writeCharCode(int charCode) → void

charCodeの文字を書き込む。

inherited

writeln([Object obj = "" ]) → void

Object.toStringを使ってobjStringに変換し、その結果に改行を加えてこれに書き込む。

inherited

プロパティ

bufferOutput ↔ bool

このHttpClientRequestがバッファ出力であるかどうかをセットまたは取得する。

read / write

connectionInfo → HttpConnectionInfo

当該クライアント接続に関する情報を取得する。

read-only

contentLength ↔ int

この要求のコンテント長を取得あるいはセットする。

read / write

cookies → List<Cookie>

このサーバに示すクッキーたち('cookie'ヘッダのなかの)

read-only

done → Future<HttpClientResponse>

HttpClientResponseFutureで、その応答が使えるようになったら完了する。

read-only, override

followRedirects ↔ bool

この要求は自動的にredirectに従うときはtrueをセットする。

デフォルトはtrueである。

read / write

headers → HttpHeaders

このクライアント要求のヘッダたちを返す。

read-only

maxRedirects ↔ int

followRedirectstrueの時に従うべきリダイレクトの最大回数をセットまたは取り出す。この回数を超過すると RedirectExceptionのエラー・イベントが付加される付加される。

read / write

method → String

該要求のPOSTとかGETなどのメソッド。

read-only

persistentConnection ↔ bool

要求されているパーシステント接続状態のセットまたは取得。

read / write

uri → Uri

該要求のURI

read-only

encoding ↔ Encoding

文字列を書き込む際に使われる Encodingのセットまたは読み出し。下位層のコンシューマ次第でこのプロパティはリード・オンリーである。

read / write, inherited

hashCode → int

このオブジェクトのハッシュ・コード。

read-only, inherited

runtimeType → Type

このオブジェクトのランタイム時の型の表現。

read-only, inherited





前のページへ

次のページ