情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

expressモジュールによるHTTPサーバ(17)コンテントネゴシエーション

この記事は「expressモジュールによるHTTPサーバ」という連載記事の17つ目の記事です。

その他の記事に関しては下を参照してください。

また、HTTP自体について知りたい方は下を参照してください。

この記事ではexpressモジュールを使ってHTTPリクエストに対するコンテントネゴシエーションを行うHTTPサーバを作成する方法について説明します。

コンテントネゴシエーション

HTTPのコンテントネゴシエーションについては下の記事を参照してください。

Acceptヘッダフィールド

HTTPリクエストのAcceptヘッダフィールドの値からクライアントがHTTPレスポンスのボディとしてどのメディアタイプ(MIMEタイプ)のデータの受け取りを希望しているかを判定するにはreq.accepts関数を使用します。

var express = require('express');

var app = express();
app.use(function (req, res, next) {
    var type = req.accepts('xxx', 'yyy');
    //or
    var type = req.accepts(['xxx', 'yyy']);
    next();
});

第1引数以降に1個以上のメディアタイプを指定します。あるいは、第1引数に1個以上のメディアタイプから成る配列を指定します。

あるいは、メディアタイプの代わりに拡張子を指定することもできます。拡張子を指定した場合には拡張子に対応するメディアタイプを指定したものと見做されます。

返り値として第1引数以降のメディアタイプの中でクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータのメディアタイプが得られます。

ただし、第1引数以降にメディアタイプの代わりに拡張子を指定した場合にはメディアタイプの代わりに拡張子が得られます。

また、第1引数以降のメディアタイプの中にクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータのメディアタイプが存在しない場合にはfalseが返ります。

Accept-Charsetヘッダフィールド

HTTPリクエストのAccept-Charsetヘッダフィールドの値からクライアントがHTTPレスポンスのボディとしてどの文字コードのデータの受け取りを希望しているかを判定するにはreq.acceptsCharsets関数を使用します。

app.use(function (req, res, next) {
    var charset = req.acceptsCharsets('xxx', 'yyy');
    next();
});

第1引数以降に1個以上の文字コードを指定します。

返り値として第1引数以降の文字コードの中でクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータの文字コードが得られます。

ただし、第1引数以降の文字コードの中にクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータの文字コードが存在しない場合にはfalseが返ります。

Accept-Encodingヘッダフィールド

HTTPリクエストのAccept-Encodingヘッダフィールドの値からクライアントがHTTPレスポンスのボディとしてどのコンテントコーディングが使用されたデータの受け取りを希望しているかを判定するにはreq.acceptsEncodings関数を使用します。

app.use(function (req, res, next) {
    var encoding = req.acceptsEncodings('xxx', 'yyy');
    next();
});

第1引数以降に1個以上のコンテントコーディングを指定します。

返り値として第1引数以降のコンテントコーディングの中でクライアントがHTTPレスポンスのボディとして受け取るデータに使用されることを希望しているコンテントコーディングが得られます。

ただし、第1引数以降のコンテントコーディングの中にクライアントがHTTPレスポンスのボディとして受け取るデータに使用されることを希望しているコンテントコーディングが存在しない場合にはfalseが返ります。

Accept-Languageヘッダフィールド

HTTPリクエストのAccept-Languageヘッダフィールドの値からクライアントがHTTPレスポンスのボディとしてどの人間の言語を対象としているデータの受け取りを希望しているかを判定するにはreq.acceptsLanguages関数を使用します。

app.use(function (req, res, next) {
    var encoding = req.acceptsLanguages('xxx', 'yyy');
    next();
});

第1引数以降に1個以上の人間の言語を言語タグとして指定します。

返り値として第1引数以降の言語の中でクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータが対象としている言語が得られます。

ただし、第1引数以降の言語の中にクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータが対象としている言語が存在しない場合にはfalseが返ります。

Acceptヘッダフィールドの値に応じた処理

HTTPリクエストのAcceptヘッダフィールドの値に応じた処理を記述するにはres.format関数を使用します。

app.use(function (req, res, next) {
    res.format({
        'xxx': function () {
        }, 
        'yyy': function () {
        }
    });
    next();
});

第1引数にオブジェクトを指定します。

このオブジェクトのプロパティの名称はメディアタイプとし、プロパティの値にはメディアタイプに対応する処理を関数として指定します。

あるいは、プロパティの名称はメディアタイプの代わりに拡張子とすることもできます。拡張子を指定した場合には拡張子に対応するメディアタイプをプロパティの名称としたものと見做されます。

このオブジェクトのプロパティの中でクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータのメディアタイプがプロパティの名称となっているプロパティの値の関数が実行されます。

ただし、HTTPリクエストにAcceptヘッダフィールドが設定されていない場合には最初のプロパティの値の関数が実行されます。

また、プロパティの中にクライアントがHTTPレスポンスのボディとして受け取りを希望しているデータのメディアタイプがプロパティの名称となっているプロパティが存在しない場合にはdefaultという名称のプロパティの値の関数が実行されるか、defaultという名称のプロパティが存在しない場合には自動的にステータスが406 Not AcceptableのHTTPレスポンスが送信されます

なお、HTTPレスポンスのContent-Typeヘッダフィールドには自動的にメディアタイプが設定されます。

サンプルコード1

クライアントからリクエストを受け取ったらコンテントネゴシエーションを行い、それに応じて様々な種類のデータを返すHTTPサーバを作成し、起動します。

Enterキーが押されたらサーバを停止し、プログラムを終了します。

express-server-accepts.js

var express = require('express');
var http = require('http');

var app = express();
app.set('env', 'development');
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.get('/author', function (req, res, next) {
    res.format({
        'text/plain': function (){
            res.send('author\'s name is pizyumi and age is 26.\r\n');
        },
        'text/html': function (){
            res.send('<p>author\'s name is pizyumi and age is 26.</p>\r\n');
        },
        'application/json': function (){
            res.json({ name: 'pizyumi', age: 26 });
        },
        'default': function() {
            res.status(406);
            res.type('text/plain; charset=utf-8');
            res.send(http.STATUS_CODES[406] + '\r\n');
        }
    });
});
app.get('*', function (req, res, next) {
    res.status(404);
    res.type('text/plain; charset=utf-8');
    res.send(http.STATUS_CODES[404] + '\r\n');
});
app.all('*', function (req, res) {
    res.status(501);
    res.type('text/plain; charset=utf-8');
    res.send(http.STATUS_CODES[501] + '\r\n');
});
var server = app.listen(3000, function () {
    console.log('http server is running...press enter key to exit.');

    process.stdin.on('data', function (data) {
        if (data.indexOf('\n') !== -1) {
            server.close(function () {
                console.log('http server closed.');
                process.exit(0);
            });
        }
    });
});
server.on('error', function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

  • express
    npm install expressでインストールします。

実行結果

実行結果の確認のためにcURLを使用しています。

コードを実行するとHTTPサーバが起動します。

C:\work\node>node express-server-accepts.js
http server is running...press enter key to exit.

cURLを使ってサーバに対してGETリクエストを行います。

C:\work\node>curl -v http://localhost:3000/author
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /author HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 41
< ETag: W/"29-7OhWUqUg7xFE+Ub0QV00gw"
< Date: Mon, 16 Jan 2017 07:20:26 GMT
< Connection: keep-alive
<
author's name is pizyumi and age is 26.
* Connection #0 to host localhost left intact
C:\work\node>curl -v http://localhost:3000/author --header "Accept: text/plain"
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /author HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: text/plain
>
< HTTP/1.1 200 OK
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 41
< ETag: W/"29-7OhWUqUg7xFE+Ub0QV00gw"
< Date: Mon, 16 Jan 2017 07:20:30 GMT
< Connection: keep-alive
<
author's name is pizyumi and age is 26.
* Connection #0 to host localhost left intact
C:\work\node>curl -v http://localhost:3000/author --header "Accept: text/html"
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /author HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: text/html
>
< HTTP/1.1 200 OK
< Vary: Accept
< Content-Type: text/html; charset=utf-8
< Content-Length: 48
< ETag: W/"30-1szZoCL1HYRYM5Aq+qqPJw"
< Date: Mon, 16 Jan 2017 07:20:31 GMT
< Connection: keep-alive
<
<p>author's name is pizyumi and age is 26.</p>
* Connection #0 to host localhost left intact
C:\work\node>curl -v http://localhost:3000/author --header "Accept: application/json"
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /author HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: application/json
>
< HTTP/1.1 200 OK
< Vary: Accept
< Content-Type: application/json; charset=utf-8
< Content-Length: 27
< ETag: W/"1b-Iau5O3VklxY2o2dZaqdEXg"
< Date: Mon, 16 Jan 2017 07:20:33 GMT
< Connection: keep-alive
<
{"name":"pizyumi","age":26}* Connection #0 to host localhost left intact
C:\work\node>curl -v http://localhost:3000/author --header "Accept: image/png"
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /author HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: image/png
>
< HTTP/1.1 406 Not Acceptable
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 16
< ETag: W/"10-KxPNv6PaywkR3iUxKhiRHw"
< Date: Mon, 16 Jan 2017 07:21:09 GMT
< Connection: keep-alive
<
Not Acceptable
* Connection #0 to host localhost left intact

サーバから正しくレスポンスが返っていることが分かります。

Enterキーを押すとサーバが停止し、プログラムが終了します。

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-express, Node.js