7
1

More than 1 year has passed since last update.

Express で レスポンスに "Content-Type" ヘッダを自前セットする方法どれを選べばいいの

Last updated at Posted at 2022-02-24

概要

Express 4 で、HTTPレスポンスヘッダーに "Content-Type" をマニュアルでセットする方法のハンズオン記録です

  • "Content-Type" ヘッダをセットする書き方はよく見かけるアプローチだけでも
    res.type, res.set, res.header, res.setHeader, res.writeHead がある
  • それぞれの書き方、結果について実際にやってみた
  • 例として、HTTPレスポンス時 の HTTPヘッダー Content-Type
    Content-Type: text/plain; charset=utf-8 となるようにコードをかいてみる

先に、全体像 → Express で HTTPヘッダをセットする方法一覧

「"Hello" を "Content-Type: text/plain;" ヘッダつけてレスポンスする」いろんなやり方

メソッド コード例 説明
res#type res.type('text/plain');
res.send('Hello');
"content-type"ヘッダー設定専用メソッド
res.type('.txt');
res.send(`Hello`);
ファイル拡張子を指定すると適切なContent-Typeを設定してくれる
res#set res.set('content-type', 'text/plain');
res.send('Hello');
ヘッダー名,値 で指定する
res.set({'content-type': 'text/plain',
'x-original-header': 'original_value'});
res.send('Hello');
res#setで、ヘッダーを複数指定する書き方
res#header res.header('Content-Type', 'text/plain');
res.send('Hello');
res#set のエイリアス
res.header({'content-type': 'text/plain',
'x-original-header': 'original_value'});
res.send('Hello');
ヘッダーを複数指定する書き方
res#setHeader res.setHeader('content-type', 'text/plain');
res.send(`Hello`);
Node.js の 'http' モジュールに所属するメソッド
res#writeHead res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('Hello');
res.end();// 応答プロセスを終了する
Node.js の 'http' モジュールに所属するメソッド
res.sendとは併用できない

本編

Expressで簡易サーバーを書く

実験用簡易サーバーを動作させて、HTTPヘッダーがどのように出力されるか curl で確認する

実験用簡易サーバー
import express from 'express';

export default class HttpServer {
  constructor() {
    this.server = null;
  }

  async start(options = {}) {
    const { port } = options;
    const app = express();

    app.get('test', (req, res) => {
      // ここでいろいろ試す
      res.set('content-type', 'text/plain');
      res.send('Hello');
    });

    return new Promise((resolve) => {
      this.server = app.listen(port, () => {
        console.log(`Server started on port:${port}`);
        resolve();
      });
    });
  }

  stop() {
    this.server.close((() => {
      console.log(`Server stopped`);
    }));
  }
}

(async () => {
  const server = new HttpServer();
  await server.start({ port: 8080 });
})()

ヘッダー確認用curl

curl --head http://localhost:8080/test

res.type

  • res#type をつかう。

  • res#type は "text/plain" のように"/"が含まれていたら、Content-Type : text/plainのように、そのまま反映するが、"/" 含まれていなければ、指定した文字列をファイル拡張子とみなして、MIMEタイプが検索され適切な Content-Typeが設定される。

コード

    app.get('test', (req, res) => {
      res.type('text/plain');
      res.send('Hello');
    });

結果

Content-Type: text/plain; charset=utf-8
  • テキストファイルの拡張子を指定してみる

コード

    app.get('/test', (req, res) => {
      res.type('.txt');
      res.send(`Hello`);
    });

結果

ちゃんと "text/plain" になっている。

Content-Type: text/plain; charset=utf-8

こうしても結果は同じだった

    app.get('/test', (req, res) => {
      res.type('txt');
      res.send(`Hello`);
    });

res.set()

コード

    app.get('/test', (req, res) => {
      res.set('content-type', 'text/plain');
      res.send('Hello');
    });

以下のように書くこともできる。こちらは、複数ヘッダをセットできる

    app.get('/test', (req, res) => {
      res.set({'content-type':'text/plain'});
      res.send('Hello');
    });

結果

HTTPヘッダーは以下のとおり。charset が自動付与されているが、Expressではデフォルトcharsetは"utf-8"となる。

Content-Type: text/plain; charset=utf-8

実際の応答はこんな感じ。ひとまず Content-Type に着目する。

curl --head http://localhost:8080/test
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain; charset=utf-8
Content-Length: 5
ETag: W/"5-9/+ei3uy4Jtwk1pdeF4MxdnQq/A"
Date: Thu, 24 Feb 2022 05:05:55 GMT
Connection: keep-alive
Keep-Alive: timeout=5

res#setでヘッダを複数指定してみる

    app.get('/test', (req, res) => {
      res.set(
        {
          'content-type': 'text/plain',
          'x-original-header': 'original_value'
        });
      res.send('Hello');
    });

結果

ちゃんと複数指定できていた。

Content-Type: text/plain; charset=utf-8
x-original-header: original_value

res.header

  • res#header は res#set のエイリアス
    なので、 res#set と等価(おんなじ)

コード

    app.get('/test', (req, res) => {
      res.header('Content-Type', 'text/plain');
      res.send('Hello');
    });

結果

Content-Type: text/plain; charset=utf-8
  • setメソッドと同じく複数指定も可
    app.get('/test', (req, res) => {
      res.header(
        {
          'content-type': 'text/plain',
          'x-original-header': 'original_value'
        });
      res.send('Hello');
    });

res.setHeader

  • res#setHeaderは Express ではなく Node.js のコアモジュールである 'http' モジュールがもつメソッド

コード

    app.get('/test', (req, res) => {
      res.setHeader('content-type', 'text/plain');
      res.setHeader('x-original-header', 'original_value');
      res.send(`Hello`);
    });

結果

Content-Type: text/plain; charset=utf-8
x-original-header: original_value

res.writeHead

  • res#writeHead は Express ではなく Node.js のコアモジュールである 'http' モジュールがもつメソッド

コード

  • res.writeHead は res.send と併用できないので注意(理由は後述)
    app.get('/test', (req, res) => {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.write('Hello');
      res.end();// 応答プロセスを終了する
    });

結果(ヘッダーぜんぶのせ)

curl --head http://localhost:8080/test
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain
Date: Thu, 24 Feb 2022 05:50:41 GMT
Connection: keep-alive
Keep-Alive: timeout=5

res.writeHeadres.sendを併用できない理由

うっかり以下のようにしてしまうとエラーとなる

    app.get('/test', (req, res) => {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.send(`hello`);
    });

エラー

 Cannot set headers after they are sent to the client

これは writeHead を実行すると、即座にレスポンスコードとヘッダ書き込みが起こるが、 res.send 内でも、再度 ヘッダ書き込みを行うため。

おまけ res.send 後に res.end は不要

    app.get('/test', (req, res) => {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.write('Hello');
      res.end();// 応答プロセスを終了する
    });

この場合は res.endで応答のプロセスを終了しているが、 res.send の場合は res.send 内で res.end をしているので、 res.end は不要となる。

おまけ、その2: Content-Type ヘッダーを送出しない方法

  • 以下のように、何も返さないで出力を終える ということもできる
    この場合、 Content-type は付与されない

コード

    app.get('/test', (req, res) => {
      res.status(200).end();
    });

結果(ヘッダーぜんぶのせ)

curl --head http://localhost:8080/test
HTTP/1.1 200 OK
X-Powered-By: Express
Date: Thu, 24 Feb 2022 05:59:52 GMT
Connection: keep-alive
Keep-Alive: timeout=5
  • こうやっても同様に何も返さないで出力をおえる

コード

    app.get('/test', (req, res) => {
      res.status(200).send();
    });

結果(ヘッダーぜんぶのせ)

curl --head http://localhost:8080/test
HTTP/1.1 200 OK
X-Powered-By: Express
Date: Thu, 24 Feb 2022 06:03:10 GMT
Connection: keep-alive
Keep-Alive: timeout=5
  • res.send で空文字を返すと Content-Type は付与される

コード例

    app.get('/test', (req, res) => {
      res.status(200).send('');
    });

結果(ヘッダーぜんぶのせ)

  • Content-Length: 0 だが、Content-Type は text/html が付与される。
>curl --head http://localhost:8080/test
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 0
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
Date: Thu, 24 Feb 2022 06:03:34 GMT
Connection: keep-alive
Keep-Alive: timeout=5

関連

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1