Old Sunset Days

Node.jsでRestifyを使ってAPIサーバー構築

日付 タグ node.js カテゴリ node.js

目次

Restifyの簡単な使い方

RestifyはRestfulなWEBサービスを構築するためのフレームワーク。Expressとは違ってViewの管理とかまで含んでおらず、REST APIサービスを作るのに適した軽量なフレームワークだ。

今回はRestifyの使い方の勉強をかねて、簡単なユーザー認証をするサーバーを作る。ユーザー認証といっても、説明を簡易にするため、DBは使わない。また、細かいセキュリティ周りは気にしないで、あくまでもRestifyの基本的な使い方の流れまでとなっている。

実用的にするには何かしらのDBと連携させたり、セキュリティを良く考慮して実装する必要があるだろう。

http://restify.com/ - Restify

A Node.js web service framework optimized for building semantically correct RESTful web services ready for production use at scale. restify optimizes for introspection and performance, and is used in some of the largest Node.js deployments on Earth.

なお、今回作ったサンプルのソースコードは参照用としてGithubの以下に置いてある。 https://github.com/hugodeblog/node-restify

Restifyのインストール

今回もまずはテストディレクトリの作成とrestifyパッケージのインストールからスタート。

$ mkdir node-restify
$ cd node-restify
$ npm init --yes
$ npm install restify --save

package.json を見てみると、本記事執筆時点ではrestify 8.5.1がインストールされたようだ。

Restifyを使ったサーバーサンプル

サーバー実装ではES6(ES2015)形式のimportを今回も用いている。


user-server.mjs

import restify from 'restify';
import * as util from 'util';

// RESTサーバーセットアップ
var server = restify.createServer({
  name: "Rest-API-Test",
  version: "0.0.1"
});

server.use(restify.plugins.authorizationParser());
server.use(check);
server.use(restify.plugins.queryParser());
server.use(restify.plugins.bodyParser({mapParams: true}));

server.listen(4000, "localhost", function() {
    console.log(server.name + ' listening at ' + server.url);
})

process.on('uncaughtException', function(err) {
  console.error('UNCAUGHT EXCEPTION: ' + (err.stack || err));
  process.exit(1);
});

function catchProcessDeath() {
  console.log('shutdown ...');
  process.exit(0);
}
process.on('SIGTERM', catchProcessDeath);
process.on('SIGINT', catchProcessDeath);
process.on('SIGHUP', catchProcessDeath);

process.on('exit', () => { console.log('exiting...'); })

// Basic認証用のためのuser, password
var basicAuthKey = { username: 'test', password: 'password'};

function check(req, res, next) {
  console.log('check was called');
  if(req.authorization && req.authorization.basic) {
    if(req.authorization.basic.username === basicAuthKey.username
      && req.authorization.basic.password === basicAuthKey.password) {
      console.log('basic authorization OK');
      next();
    }
    else {
      res.send(401, {'message': 'Not authorized'});
      next(false);
    }
  }
  else {
    res.send(500, {'message': 'No authorization key'});
    next(false);
  }
}

// テスト用のユーザーを作成
const userList = [
  {user: 'me', password: 'me123'},
  {user: 'you', password: 'you123'}
];

// 該当ユーザーがいるかチェックする
function findUser(username, password) {
  for(let person of userList) {
    if(person.user === username && person.password === password) {
      return {'user': person.user, 'password': person.password};
    }
  }
  return;
}

// ユーザー、パスワードチェック
server.post('/password-check', (req, res, next) => {
  console.log(`req.params.username = ${req.params.username}`);
  console.log(`req.params.password = ${req.params.password}`);
  const user = findUser(req.params.username, req.params.password);
  console.log(`user=${user}`);
  if (user == undefined) {
      res.send(401, {'auth_check': 'NG', 'message': 'User/Password incorrect'});
      next(false);
  } else {
      res.send(Object.assign(user, {'auth_check': 'OK'}));
      next(false);
  }
});

では、ちょっとコードの作りを見ておこう。

var server = restify.createServer({
  name: "Rest-API-Test",
  version: "0.0.1"
});

サーバー名とサーバーバージョンを指定してサーバーの作成。

続いてRestifyのプラグンの設定。

http://restify.com/docs/plugins-api - Restify Plugin

authorizationParser
Parses out the Authorization header as best restify can.
Currently only HTTP Basic Auth and HTTP Signature schemes are supported.


server.use(restify.plugins.authorizationParser());
認証方式ヘッダーだが、HTTP Basic AuthとHTTP Signatureだがけ今の所利用可能のようだ。

server.use(restify.plugins.queryParser());
リクエストパラーメーターを使用して、req.queryでクエリパラメーターにアクセスが可能。

server.use(restify.plugins.bodyParser({mapParams: true}));
POST通信で渡されたリクエストボディが、req.bodyでアクセスが可能。
{mapParams: true}を使用することで、req.paramsでもアクセスできるようになる。


var basicAuthKey = { username: 'test', password: 'password'};

function check(req, res, next) {
  console.log('check was called');
  if(req.authorization && req.authorization.basic) {
    if(req.authorization.basic.username === basicAuthKey.username
      && req.authorization.basic.password === basicAuthKey.password) {
      console.log('basic authorization OK');
      next();
    }

Basic認証用のユーザーとパスワードチェック。 next(); で次の処理に進ませる。

Basic認証が失敗した場合は

else {
   res.send(401, {'message': 'Not authorized'});
   next(false);
 }

401エラーを返してnext(false); でサーバー側での処理チェーンを終わらせる。


// テスト用のユーザーを作成
const userList = [
  {user: 'me', password: 'me123'},
  {user: 'you', password: 'you123'}
];

// 該当ユーザーがいるかチェックする
function findUser(username, password) {
  for(let person of userList) {
    if(person.user === username && person.password === password) {
      return {'user': person.user, 'password': person.password};
    }
  }
  return;
}

// ユーザー、パスワードチェック
server.post('/password-check', (req, res, next) => {
  console.log(`req.params.username = ${req.params.username}`);
  console.log(`req.params.password = ${req.params.password}`);
  const user = findUser(req.params.username, req.params.password);
  console.log(`user=${user}`);

userListは仮で登録したユーザー名、パスワード。本来はDBに入れておくデータだが、ここはサンプルプログラムなので配列に入れた決め打ちのユーザー。

findUserはユーザー名、パスワードが一致するかのチェックルーチン。

/password-check がサーバーがPOST処理を受け付けるAPIである。

以上がざっとしたプログラムの概要説明。

Restifyの動作チェック検証

では、実際にAPIサーバーとして機能しているのか、サーバーを立ち上げて、curlコマンドを使ってサーバーにリクエストを投げて動作検証をしてみる。

$ node user-server.mjs
Rest-API-Test listening at http://127.0.0.1:4000

サーバーが起動して4000番ポートで待ち受けした状態。

そこで別のコンソール窓からcurlコマンド実行。

  • POST先URLがそもそも間違っているケース
$ curl -i -X POST -H "Content-Type: application/json" \
> --user "test:password" \
> -d '{"username":"me", "password":"me123"}' \
> http://localhost:4000/password
HTTP/1.1 404 Not Found
Server: Rest-API-Test
Content-Type: application/json
Content-Length: 64
Date: Fri, 11 Dec 2020 13:57:16 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"code":"ResourceNotFound","message":"/password does not exist"}
  • Basic認証用のユーザー、パスワードが合致しない場合
$ curl -i -X POST -H "Content-Type: application/json" \
> --user "tes:password" \
> -d '{"username":"me", "password":"me123"}' \
> http://localhost:4000/password-check
HTTP/1.1 401 Unauthorized
Server: Rest-API-Test
Content-Type: application/json
Content-Length: 28
Date: Fri, 11 Dec 2020 14:00:22 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"message":"Not authorized"}
  • Basic認証は合っているけど、/password-check にPOSTされたユーザー、パスワードが一致しない場合
$ curl -i -X POST -H "Content-Type: application/json" \
> --user "test:password" \
> -d '{"username":"me", "password":"me1234"}' \
> http://localhost:4000/password-check
HTTP/1.1 401 Unauthorized
Server: Rest-API-Test
Content-Type: application/json
Content-Length: 55
Date: Fri, 11 Dec 2020 14:04:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"auth_check":"NG","message":"User/Password incorrect"}
  • Basic認証も/password-check にPOSTされたユーザー、パスワードも一致する場合
$ curl -i -X POST -H "Content-Type: application/json" \
> --user "test:password" \
> -d '{"username":"me", "password":"me123"}' \
> http://localhost:4000/password-check
HTTP/1.1 200 OK
Server: Rest-API-Test
Content-Type: application/json
Content-Length: 50
Date: Fri, 11 Dec 2020 14:05:06 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"user":"me","password":"me123","auth_check":"OK"}

エラーが生じるケース、問題なく結果が帰ってくるケースのどちらも大丈夫なようだ。 以上、簡単ではあるが、Restifyの基本的な流れは確認できたかと思う。