接近戦に強いExpress入門　～Webサーバー、テンプレートエンジン、セッション、BASIC認証～ 

**2021年10月4日更新**: 最新のExpressは"body-parser"が内蔵になった件

# 概要
[**Express**](https://www.npmjs.com/package/express)はNode.jsの**定番Webフレームワーク**です。本稿では[**Express**](https://www.npmjs.com/package/express)でやりたい **あんなこと、こんなこと** を<font color=red>ちゃんと仕組みを理解しつつ</font>ハンズオンで試します。ゼロからはじめるので、node.js環境さえあればOKです。

**やりたいこと**

- 静的コンテンツを表示するWebサーバーをつくる
- 指定されたパスにアクセスがあったら動的に応答する
- Expressの仕組みを理解する
- アクセスログを取得する
- [WebAPIのようにJSONを返す](#webapiのようにjsonを返す)
- RESTFullなAPIのようにURL内にパラメータを入れる
- ファイルをまるごと返す
- テンプレートエンジンを使う
- セッションを使う
- BASIC認証を使う

## 想定読者
- **node.js**や**npm**をつかったことがある人

## 対象環境
- **express 4.17.1**

## ソースコード
紹介する全ソースコードは以下にあります
https://github.com/riversun/express-examples

以下のようにすれば、すぐに試すことができます

```shell
git clone https://github.com/riversun/express-examples.git
npm install
npm start
```

# 準備(Expressをインストール)

- プロジェクト用のディレクトリを作る

ゼロから使えるようにするので、まずプロジェクト用のディレクトリから作る。

```
mkdir webserver
cd webserver
```

- npm init する

npm initをしてnpmプロジェクトを初期化する。

エンターを9回押せばOK。すると、**package.json**が自動生成される。

```shell

npm init

package name: (webserver)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /webserver/package.json:

{
  "name": "webserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) yes
```

- Express をインストールする

```shell
npm install express
```

これで Expressが使えるようになった。

# 静的Webサーバーを作る

さっそく、静的ファイルをホスティングできるWebサーバーを作る。
ひとまずは、今作ったプロジェクトのカレントディレクトリ以下をWebとして公開する。

静的なファイルをホスティングするコードは以下のとおり。とっても簡単。

```js:webserver.js
const express = require("express");

const app = express();
const port=8080;

app.use(express.static('./'));

app.listen(port, ()=> {
    console.log('Server started on port:'+port);
});
```
<hr>
コードで注目するべきポイントは以下。
静的ファイルを配信する場合はこのように**app.use**で**express.static**を使う

```js
app.use(express.static('./'));
```

[**app.use**](https://expressjs.com/en/4x/api.html#app.use)の中身に指定されているのは**express.static**だが、これの実体は **function(req,res,next){}**の形式の関数で、この関数のなかで、req(＝ブラウザからのrequest)をハンドリングして、指定されたパスに相当する静的ファイルがみつかればそれを返すという処理をしている。

**express.static**のようにリクエストに応じた処理をしてくれる**function**を**middleware**と呼ぶ。後述するが、ここでは要するに**middleware**は、**「あるリクエストを横取りして処理してくれる関数」**と覚えておく。


### package.jsonに起動コードを書く

package.jsonに起動コードを書く。

```json:package.json
{
  "name": "webserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start":"node webserver.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4"
  }
}
```

追加した部分はここ。

```json
    "start":"node webserver.js",
```

### index.html を書く

表示のために、index.htmlを作る。

```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hello express web server!</h1>
</body>
</html>
```

### 実行する

```
npm start
```

```shell
Server started on port:8080
```

これでwebサーバーが起動できた！

http://localhost:8080 にアクセス

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/d8be3493-0357-e8bb-66c1-c3477c91a26b.png)

無事に表示できた！

ご参考：**express.static**で**404エラーを出したいとき**

ちなみに、ファイルがみつからなかったときはどうするか。
その場合の挙動を [**fallthrough**](https://expressjs.com/tr/4x/api.html#fallthrough)というオプションで指定することができる。

```js
app.use(express.static('./', {fallthrough: true}));
```

**fallthrough=true**の場合は、ファイルがみつからない場合は**express.static**の内部で**next()**が呼ばれる。つまり、そのまま次の処理に移行するので特にエラーなどは発生しない。
**fallthrough=false**の場合は、内部で**next(err)**が呼ばれ、エラー扱いとなる。
デフォルトは**falthrough=true**となる。

もし、ExpressをWebサーバーとして使いたくて、かつ独自の404エラーを返したい場合は、**fallthrough=true**として、**express.static**処理終了後の移行先として **app.use**でエラー処理をするコードを書けば良い。（以前はfallthroughは「app.router で受けよう」みたいなやり方が推奨されていたが、今は使えなくなった）

https://github.com/expressjs/express/issues/3830


# 指定されたパスへのアクセスに対応する

静的ファイルだけでなく、指定されたパスへのアクセスに対応するため以下のようにした。

```js:webserver.js
const express = require('express');

const app = express();
const port = 8080;

app.use(express.static('./'));

app.get('/test', function (req, res) {
    res.send('This is a test!');
});

app.get('/*', function (req, res) {
    res.send('<html><body><h1>Others</h1></body></html>');
});

app.listen(port, () => {
    console.log('Server started on port:' + port);
});
```

追加した部分はこうなる。

```js

app.get('/test', function (req, res) {
    res.send('This is a test!');
});

app.get('/*', function (req, res) {
    res.send('<html><body><h1>Others</h1></body></html>');
});
```

[**app.get**](https://expressjs.com/en/4x/api.html#app.get.method)は「**HTTP GETリクエスト**をハンドリングするよ」という意味でここでは、**/test**というパスと**/\***というパスを指定している。
上記パスにアクセスがあったときにはコールバックとして**function(req,res)**が実行されるが、そのfunction内で[**res.send**](https://expressjs.com/en/4x/api.html#res.send)によってテキストをレスポンスとして返す。ちなみに、**res.send(テキスト)**のようにテキストを返すときは**Content-Type**は自動的に**text/html**となる。

ということで **http://localhost:8080/test**にGETリクエストがあれば**This is a test!**を返し、それ以外のパス、たとえば**http://localhost:8080/hoge**にアクセスがあれば**Others**を返す。


実行してみると、以下のようにうまく動いた。

http://localhost:8080/test
![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/b9ce7c94-0b6f-1d9b-c16f-f2db818566d3.png)


http://localhost:8080/hoge
![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/73a91564-e166-60c9-a9b2-033fa3523e5e.png)

HTTP GET以外のメソッドに対応させる場合

**POST**は[**app.post**](https://expressjs.com/en/4x/api.html#app.post.method)、**PUT**は[**app.put**](https://expressjs.com/en/4x/api.html#app.put.method)、**DELETE**は[**app.delete**](https://expressjs.com/en/4x/api.html#app.delete.method)をそれぞれ使えば良い。

# <font color=red>**app.use**</font>とは何か(ついでに、Expressの動作の仕組みも学ぶ）

さて、やや静的Webサーバーの話から横道にそれるが（でも必要な知識を）あるリクエストを横取りして処理してくれる**middleware**について、もう少し仕組みを理解したい。

Expressは概略すると以下のような仕組みで動いている。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/05aed06b-47e3-3055-9125-ac4f675f960c.png)

あるパス(例えば<font color=red> **/**</font>)にリクエストがあると、そのパスを担当するモジュールがそのリクエストを受け付けて、何らかの処理をしてレスポンスを返す。このモジュールを**Router(ルーター)**と呼ぶ。

```js
const app = express();
```

とやると、 app の中にはひそかに **Router** ができている。なので、まず1つ**Router**が準備された気になって次をみていく。

この**Router**には、「Routerにやって来たリクエストを横取りして処理させる人」を追加することができ、これが前述の**middleware**となる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/2f8c7a6b-e27b-90a3-4abd-6cf495214e39.png)


ようやく登場するが、このrequestを横取りさせるために、**middleware**を登録する方法が <font color=red>**app.use**</font>となる。


```js:app.useの使い方

app.use(function (request, response, next) {
  next();
});
```

のようにすると、**Router**に来たリクエストが、この**function(req,res,next){}**関数を通過する。

この関数の中で**request**を分析して何か処理をしたり、そのまま**response**を返したりしてOK。

では、この中にある**next()**は何かというと、**next()**を実行すると次に処理を移行させることができる。

```js
next();
```

というのも、**middleware**は複数登録することが可能となっている。

以下のようにすると、funcA→funcBの順番で**middleware**の関数が実行される。
（ただし各**middleware**で**next()**が実行されないと処理はそこで止まる)

```js
app.use(funcA);
app.use(funcB);
app.get('/test', function (req, res) {
    res.send('<html><body><h1>Test</h1></body></html>');
});
```

**app.use**をすると、Expressの**Router**の内部では、**middleware**が**Router**がもつスタックに積まれる。複数回の**app.use**を実行すればそれがスタックに積まれて、リクエストがあったときに順番に実行されていく。(「リクエスト処理のパイプライン」などといわれることもある）

図にするとこんなイメージ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/5375dacf-9525-1f37-196d-d5d04bb551bf.png)


HTTP GETメソッドに反応する **app.get**（や HTTP POSTメソッドに反応する **app.post**なども）内部的には同じように**Router**が持つスタックにつまれる。

**app.use**には以下のようにあるパスを指定して、そのパスに反応するように指定することもできるが、**app.use**の場合はたとえば**/mypath**を指定した場合**/mypath/path1**のように指定したパス以下にも反応する。

```js
app.use( "/mypath" , myfunc);
```

## Routerを追加することも可能

さきほど、ひそかに(=暗黙的に）アプリケーション用の**Router**ができている旨書いたが、以下のように**app.Router()**とすることで、明示的に新しい**Router**を生成できる。

```js
const newRouter1=app.Router();
const newRouter2=app.Router();
app.use('/path1', newRouter1);
app.use('/path2', newRouter2);
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/e4b3b243-0f0e-d552-eab2-0b6b1884677d.png)

このように**Router**と**middleware**の概念がつかめると、Expressはかなりシンプルなアーキテクチャであることがわかる。（横道だがJavaの軽量サーバーであるJettyのアーキテクチャもこれにかなり近い。あちらはmiddlewareのことをhandlerと読んでいる）

middlewareの詳しい使い方の例は[ここ](https://expressjs.com/en/4x/api.html#middleware-callback-function-examples)を参照
https://expressjs.com/en/4x/api.html#middleware-callback-function-examples

#WebAPIのようにJSONを返す

以下のようにすれば、JSONフォーマットでレスポンスされる。
**Content-Type**は自動的に**application/json; charset=utf-8**となる。

（以下は、POSTで JSONテキストが送信されてくることを想定する）

```js
app.set('json spaces', 2);
app.get('/api', function (req, res) {
    res.json({name: 'John Doe', attr: {age: 30, sex: 'male'}});
});

app.post('/api', function (req, res) {

    const posted = req.body;
    console.log('request body=' + JSON.stringify(posted));

    res.json({name: 'John Doe', attr: {age: 30, sex: 'male'}});
});
```

**app.set('json spaces', 2);**（※）によってインデント幅を**スペース2個ぶん**と指定しているが、これを指定することでJSONがフォーマットされる。

※[**app.set**](http://expressjs.com/en/4x/api.html#app.set)は**アプリケーション設定プロパティ**(application setting property)という。

これを webserver.jsに追加して http://localhost:8080/api にアクセスすると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/2a619186-de40-360c-3eb9-8b684f6da4a8.png)

JSONがフォーマットもされてレスポンスされた。

~~ちなみに、expressでPOSTのリクエストボディを処理するには **bodyparser**をインストールして、ミドルウェアを有効化する必要がある~~

ちなみに、expressでPOSTのリクエストボディを処理するには内蔵されているbodyParserを有効化する。

express v4.16より前のバージョンではPOSTのリクエストボディを処理するために外部の **body-parser** というパッケージの導入が必要だったが、v4.16以降はexpressに内蔵された。（内蔵されているのは body-parser そのものではある）

以下でbodyparserを有効にする

```js
app.use(express.urlencoded({extended: true}));// "application/x-www-form-urlencoded"なデータが送信される場合にはこれが必要
app.use(express.json()); // JSONテキストが送信される場合にはこれが必要
```

express.urlencodedとexpress.jsonの挙動を詳しく理解したければ body-parserのソース([urlencoded](https://github.com/expressjs/body-parser/blob/master/lib/types/urlencoded.js),[json](https://github.com/expressjs/body-parser/blob/master/lib/types/json.js))を読むと良い。

すると

```js
req.body
```

でリクエストボディを取得できる


### URLに含まれる動的パラメータを取得する

RESTFullなAPIのように http://localhost:8080/users/3000 のようにアクセスできるようにするには以下のようにする。

```js

app.get('/users/:userId', function (req, res) {
    res.json({name: `John Doe`, userId: `${req.params.userId}`, attr: {age: 30, sex: 'male'}});
});
```

**/users/:userId**の**:userId**の部分が動的パラメータとなる。取得するには**req.params.userId**のようにする。

これを webserver.jsに追加して http://localhost:8080/users/3000 にアクセスすると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/09986de6-824a-ff31-e128-78d769d151b0.png)

**userId**の部分を抜き出すことができている。


### アクセスログを取得する

共通処理として、各リクエストがあったときに、ログを取得できるようにする。
そのために前述した**app.use**をつかって以下のように**middleware**として実装する。

```js

app.use(function (req, res, next) {
    const reqUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    const reqDate = new Date();
    const srcIpAddr = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    console.log(`${reqDate} Access '${reqUrl}' from ${srcIpAddr}`);
    next();
});
```

最後の**next()**で次の処理に移る。

コードは以下のようにログ取得用の**middleware**を登録する**app.use**は上部に書いておく。

```js:webserver.js
const express = require('express');

const app = express();
const port = 8080;

app.use(function (req, res, next) {
    const reqUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    const reqDate = new Date();
    const srcIpAddr = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    console.log(`${reqDate} Access '${reqUrl}' from ${srcIpAddr}`);
    next();
});

app.use(express.static('./'));

app.get('/test', function (req, res) {
    res.send('This is a test!');
});

...省略...

app.listen(port, () => {
    console.log('Server started on port:' + port);
});
```

これで**npm start**して実際に以下のURLアクセスしてみると、ログ用の**middleware**が動作して、ちゃんとログがとれている。

http://localhost:8080/index.html
http://localhost:8080/users/3000

```shell
npm start
Server started on port:8080
Sat Feb 23 2019 21:03:54 GMT+0900 (東京 (標準時)) Access 'http://localhost:8080/index.html' from ::1
Sat Feb 23 2019 21:04:01 GMT+0900 (東京 (標準時)) Access 'http://localhost:8080/users/3000' from ::1
```

# ファイルを指定してレスポンスする

[**res.sendFile**](https://expressjs.com/en/4x/api.html#res.sendFile)をつかうと、ファイルを送る（レスポンス）することができる。**Content-Type**はファイル名の拡張子から判定される。

```js
app.get('/file' , function(req, res){
    res.sendFile(__dirname+'/index.html');
});
```

# テンプレートエンジンを使う

expressはデフォルトで[**pug**](https://pugjs.org/api/getting-started.html)、[**mustache**](https://www.npmjs.com/package/mustache)、[**EJS**](https://www.npmjs.com/package/ejs)といったテンプレートエンジンに対応している。
ここでは[**pug**](https://pugjs.org/api/getting-started.html)をつかってテンプレートエンジン機能をためす。

pugをインストールする

```shell
npm install pug
```

package.jsonは以下のようになる

```json:package.json
{
  "name": "webserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node webserver.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4",
    "pug": "^2.0.3"
  }
}
```

** "pug": "^2.0.3"***が追加された。

次に**pug**のテンプレートを記述する。こんな感じで。

```pug:index.pug
html
    head
        title= mytitle
    body
        h1= mymessage
```

次に**webserver.js**を編集していく。

```js
app.set('view engine', 'pug')
app.set('views', './')

app.get('/pug', function (req, res) {
    res.render('index', { mytitle: 'My Title', mymessage: 'Hi, Pug!' })
});
```

<hr>

上記コードだが、まず**express**が使うテンプレートエンジンをアプリケーション設定プロパティで指定する。

```js:expressで使うテンプレートエンジンの種類を指定する
app.set('view engine', 'pug')
```
でテンプレートエンジンとして**pug**を指定する。

<hr>

次に、**pug**のテンプレートファイルを置くディレクトリを指定する。さっきつくった、**index.pug**の置き場所となる。デフォルトでは**./views**だが、ここではルートディレクトリ **./**を指定する。

```js:pugのテンプレートファイルを置くディレクトリ

app.set('views', './')
```

<hr>

次に、テンプレートにある **mytitle**と**mymessage**に値を入れる。

```js
app.get('/pug', function (req, res) {
    res.render('index', { mytitle: 'My Title', mymessage: 'Hi, Pug!' })
});
```

ここでは、```res.render('index', { mytitle: 'My Title', mymessage: 'Hi, Pug!' })```で**index**という名前のテンプレートファイルの、**mytitle**と**mymessage**に値を入れている。

さて、これで**npm start**して http://localhost:8080/pug にアクセスすると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/482c12e6-1ca4-79dd-db4f-da4e24fe4e44.png)

うまく動作した。

こういう↓ソースが生成される

```html
<html><head><title>My Title</title></head><body><h1>Hi, Pug!</h1></body></html>
```

テンプレートエンジンの動作についてより詳しくは[ここ](http://expressjs.com/en/advanced/developing-template-engines.html)を参照
http://expressjs.com/en/advanced/developing-template-engines.html


# セッションを使う

ここではセッションの使い方をみていく。
ブラウザで更新ボタンをおすとカウントアップしていく例をみる。

セッションとは、複数回のHttpリクエスト-レスポンスのやりとりを一連の**セッション**という概念で取り扱うためのもので、その実現には大昔からCookieが使われている。

expressでセッション機能を使うために、このCookieの使い方の違いで2つのアプローチがあり、Cookieはセッションの識別用にだけ使いデータはサーバーに格納する方式の[**express-session**](https://www.npmjs.com/package/express-session)というモジュールと、Cookieにセッションのデータそのものを格納する方式の[**cookie-session**](https://www.npmjs.com/package/cookie-session)というモジュールが利用できる。

それぞれの特徴は以下のとおり。

<table>
<tr><td>モジュール</td><td>セッション情報の保存場所</td><td>Cookieの利用方法</td></tr>
<tr><td><b><a href="https://github.com/expressjs/session">express-session</a></b></td><td>サーバー</td><td>セッションの識別子(sessionId)だけをCookieに保存する。セッションデータはサーバーサイドのDB等に保存する。</td></tr>
<tr><td><b><a href="https://github.com/expressjs/cookie-session">cookie-session</a></b></td><td>クライアント（ブラウザ）</td><td>セッションのデータそのものをCookieに保存する。そのためサーバー側にDB等の準備は不用。</td></tr>
</table>


本稿では[**express-session**](https://github.com/expressjs/session)を使うのでさっそくインストール。

```shell
npm install express-session
```

これで、[**express-session**](https://github.com/expressjs/session)がインストールできたので、コードを書いていく。
ブラウザを更新するとカウントアップする例となる。

```js:webserver.js抜粋（セッションをつかってカウントアップしていく）
const session = require('express-session');
const memoryStore = new session.MemoryStore;

const sess = {
    secret: 'mysecret',
    resave: false,
    saveUninitialized: true,
    cookie: {maxAge: 1000 * 60 * 60 * 24 * 30},
    store: memoryStore
};

if (app.get('env') === 'production') {
    app.set('trust proxy', 1); // trust first proxy
    sess.cookie.secure = true; // serve secure cookies
}
app.use(session(sess));

app.get('/counter', function (req, res, next) {
    console.log(req.session.counter);
    if (req.session.counter) {
        req.session.counter++;
    } else {
        req.session.counter = 1;
    }

    res.send(`<html><body><p>${req.session.counter}</p>
              <p>expires in:${req.session.cookie.maxAge / 1000}s</p></body></html>`);
});

```
<hr>

コードを上からみていく。

```js
const sess = {
    secret: 'mysecret',
    resave: false,
    saveUninitialized: true,
    cookie: {maxAge: 1000 * 60 * 60 * 24 * 30},
    store: memoryStore
};
```

**secret**:sessionIdへの署名（暗号化）に必要。任意の文字列で指定する。
**resave**:リクエストを通してセッションが変更されなくても強制的にセッション情報をセッションストアに保存するか否か。
**saveUninitialized**:初期化されていないセッションを強制的にセッションストアに保存するか否か。
**cookie.maxAge**:cookieの有効期限。ここでは30日をセットしている。
**store**:cookieのストア（保存先）を指定している。デフォルトでは MemoryStore となるが明示的に指定した。本番環境では永続化のためデータベースを指定するのが一般的。ストアの種類は[**ここ**](https://github.com/expressjs/session#compatible-session-stores)を参照。

<hr>

次はここ。ここでは本番環境で実行されるときのみセットされる設定を記述している。

```js
if (app.get('env') === 'production') {
    app.set('trust proxy', 1); // trust first proxy
    sess.cookie.secure = true; // serve secure cookies
}
app.use(session(sess));
```

**app.set('trust proxy', 1);**は「プロキシーサーバーからn番目のホップをクライアントとして信頼するよ」という設定となる。（※）

`sess.cookie.secure`は `sess={cookie:{secure:true}}`を設定しているのと同等だが、**secure:true**はセキュアなCookieを使うという設定だが、これは「**HTTPS**ベースのリクエストの場合のみCookieが有効になる」という設定となる。また前述の**trust proxy**の設定を同時にしておく必要がある。

※ **trust proxy**が適切に設定されていないと、例えばアクセスログを取るときにクライアント（ユーザー）がプロキシー経由でアクセスしてきたとき、誤ってプロキシーサーバーのIPアドレスをクライアントのIPアドレスとしてしまう。詳しくは[ここ](http://expressjs.com/en/guide/behind-proxies.html)。

**app.use(session(sess));**でセッションを有効にする（＝セッション用処理用のミドルウェアを登録する）

<hr>


次はここで実際にカウントアップする。
セッションオブジェクトは **req.session** になるので、ここにデータをセットしていけばOK。

```js:セッションをつかってカウントアップする
app.get('/counter', function (req, res, next) {
    console.log(req.session.counter);
    if (req.session.counter) {
        req.session.counter++;
    } else {
        req.session.counter = 1;
    }

    res.send(`<html><body><p>${req.session.counter}</p>
              <p>expires in:${req.session.cookie.maxAge / 1000}s</p></body></html>`);
});
```

これを実行して、 http://localhost:8080/counter にアクセスし、
更新ボタンをおしていくとセッションに記憶されたカウンター変数がカウントアップされていく。

![dd2.gif](https://qiita-image-store.s3.amazonaws.com/0/170905/ed8fc19d-f4fe-50ca-77c7-585291420898.gif)

# BASIC認証を使う

BASIC認証も比較的簡単に導入できる。BASIC認証には、[**basic-auth**](https://www.npmjs.com/package/basic-auth)というモジュールを使う。

```
npm install basic-auth
```


ここでは、あるURL以下(**/admin**)だけにBASIC認証をかける例をみていく。
コードは以下のようになる。

```js:webserver.jsの抜粋
const authApp = express.Router();
app.use('/admin', authApp);

const basicAuth = require('basic-auth');

authApp.use(function (req, res, next) {
    const credentials = basicAuth(req);
    if (!credentials || (credentials.name !== 'admin' || credentials.pass !== 'mypassword')) {
        const REALM = 'secure!';
        res.header('WWW-Authenticate', 'Basic realm="' + REALM + '"').status(401).end('Access denied');
    } else {
        next();
    }
});

authApp.get('/test', function (req, res) {
    res.send('<html><body><h1>Auth Page</h1></body></html>');
});

authApp.get('/*', function (req, res) {
    res.send('<html><body><h1>Others in auth page</h1></body></html>');
});
```

<hr>

```js
const authApp = express.Router();
app.use('/admin', authApp);
```

あるURL以下だけに処理をかけたいので、[**Router**](https://expressjs.com/en/4x/api.html#express.router)をつかう。**express.Router()**とすると新しいRouterを生成できる。
**app.use('/admin', authApp);**にて、**/admin\***というパス名をもったURLにアクセスがあったら、**authApp**というRouterを使うように設定する。**/admin/test**のようなURLの場合もこのRouterが使われる。

<hr>

次はBASIC認証のコードとなる

```js
const basicAuth = require('basic-auth');

authApp.use(function (req, res, next) {
    const credentials = basicAuth(req);
    if (!credentials || (credentials.name !== 'admin' || credentials.pass !== 'mypassword')) {
        const REALM = 'secure!';
        res.header('WWW-Authenticate', 'Basic realm="' + REALM + '"').status(401).end('Access denied');
    } else {
        next();
    }
});
```

**authApp.use**で、**/admin\***用のルーターにBASIC認証処理の**middleware**を実装している。

**const credentials = basicAuth(req);**でユーザーからのリクエストに格納されているBASIC認証の**Credential**（ここでは、ユーザー名とパスワード）を取得することができる。
もし、**Credential**が指定されていない　または、　ユーザー名・パスワードの組み合わせが間違っていれば、以下のようにBASIC認証を求めるレスポンスを返す。

```js
res.header('WWW-Authenticate', 'Basic realm="' + REALM + '"').status(401).end('Access denied');
```

一方、**Credential**がOKなら**next()**で次の処理に移行する。

<hr>

あとはBASIC認証をかけるURLと処理内容を書いていけばOK。

```js
authApp.get('/test', function (req, res) {
    res.send('<html><body><h1>Auth Page</h1></body></html>');
});

authApp.get('/*', function (req, res) {
    res.send('<html><body><h1>Others in auth page</h1></body></html>');
});
```

`authApp.get('/test', ...`は、**/admin**用のRouterにセットされているのでアクセスするときには http://localhost:8080/admin/test となる。

さっそくBASIC認証のページにアクセスしてみると、以下のように、BASIC認証ダイアログが表示される。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/96174b56-1183-9088-991c-a624b818ad09.png)

コードで指定した **admin** と **mypassword**とそれぞれ入力すると無事ページが表示された。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/170905/60a751d9-61b0-690f-837f-fa57dc0117a2.png)

# コードの全体像

これまでの実装した機能を全部入りにしたコードは以下のようになります

```js:webserver.js
const express = require('express');

const session = require('express-session');
const memoryStore = new session.MemoryStore;

const app = express();
const authApp = express.Router();

const port = 8080;

//middleware for logging
app.use(function (req, res, next) {
    const reqUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    const reqDate = new Date();
    const srcIpAddr = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    console.log(`${reqDate} Access '${reqUrl}' from ${srcIpAddr}`);
    next();
});

//middleware for session
const sess = {
    secret: 'mysecret',
    resave: false,
    saveUninitialized: false,
    cookie: {maxAge: 1000 * 60 * 60 * 24 * 30},
    store: memoryStore
};

if (app.get('env') === 'production') {
    app.set('trust proxy', 1); // trust first proxy
    sess.cookie.secure = true; // serve secure cookies
}
app.use(session(sess));

//middleware for static pages
app.use(express.static('./'));

//router for basic authentication pages
app.use('/admin', authApp);


app.get('/test', function (req, res) {
    res.send('This is a test!');
});

//provide json api
app.set('json spaces', 2);
app.get('/api', function (req, res) {
    res.json({name: 'John Doe', attr: {age: 30, sex: 'male'}});
});

app.get('/users/:userId', function (req, res) {
    res.json({name: `John Doe`, userId: `${req.params.userId}`, attr: {age: 30, sex: 'male'}});
});

//provide file
app.get('/file', function (req, res) {
    res.sendFile(__dirname + '/index.html');
});

//provide pages generated by template engine
app.set('view engine', 'pug');
app.set('views', './');
app.get('/pug', function (req, res) {
    res.render('index', {mytitle: 'My Title', mymessage: 'Hi, Pug!'});
});

//provide session page
app.get('/counter', function (req, res, next) {
    console.log(req.session.counter);
    if (req.session.counter) {
        req.session.counter++;
    } else {
        req.session.counter = 1;
    }

    res.send(`<html><body><p>${req.session.counter}</p>
              <p>expires in:${req.session.cookie.maxAge / 1000}s</p></body></html>`);
});

app.get('/*', function (req, res) {
    res.send('<html><body><h1>Others</h1></body></html>');
});


//provide basic-auth pages
const basicAuth = require('basic-auth');
authApp.use(function (req, res, next) {
    const credentials = basicAuth(req);
    if (!credentials || (credentials.name !== 'admin' || credentials.pass !== 'mypassword')) {
        const REALM = 'secure!';
        res.header('WWW-Authenticate', 'Basic realm="' + REALM + '"').status(401).end('Access denied');
    } else {
        next();
    }
});

authApp.get('/test', function (req, res) {
    res.send('<html><body><h1>Auth Page</h1></body></html>');
});

authApp.get('/*', function (req, res) {
    res.send('<html><body><h1>Others in auth page</h1></body></html>');
});

app.listen(port, () => {
    console.log('Server started on port:' + port);
});


```

# まとめ

- Expressはとてもシンプルに強力な機能を実装できることがわかりました。今後も出番が多そうです。

- 紹介した全ソースコードは以下にあります
https://github.com/riversun/express-examples

- 以下のようにすれば、すぐに試すことができます

```shell
git clone https://github.com/riversun/express-examples.git
npm install
npm start
```



**サーバー起動後のお試しURL**

- 静的ファイルをホストする
http://localhost:8080

- 指定のURLパスでアクセスする
http://localhost:8080/test

- JSONでかえす
http://localhost:8080/api

- 動的のURLパラメータ
http://localhost:8080/users/3000

- テンプレートエンジンを使う
http://localhost:8080/pug

- セッションを使う
http://localhost:8080/counter

- BASIC認証を使う(user:admin,pw:mypassword)
http://localhost:8080/admin/test