Nginxのリバースプロキシを用いてRESTでないAPIをRESTっぽく呼び出せるようにする

このエントリーは、エキサイト Advent Calendar 2016 の12/8の記事です。

こんにちは。ニュースチームのエンジニアのツボイです。


f0364156_10224756.png

ニュースチームで使っているAPIの中には、RESTでないAPIがあります。修正するとなると結構工数がかかってしまうので、今回はNginxでAPIゲートウェイを作成し、既存APIをRESTっぽく呼び出せるような実装を検討してみました。
Nginxを使用した理由は最近Nginxを触る機会があったことと、実装する上で想像しやすかったためです。


API設計 ※実際に使用しているAPIとは多少異なります

エンドポイント
http://api.example.com/v1/api/

IPアドレス
192.168.161.100

GET /v1/api/news/list/
ニュース記事一覧を取得する。

ParameterRequired
category任意


GET /v1/api/news/detail/
ニュース記事詳細を取得する。

ParameterRequired
id必須
category任意


GET /v1/api/category/list/
カテゴリ一覧を取得する。

ParameterRequired
なし


GET /v1/api/news/movie/list/
ニュース動画一覧を取得する。

ParameterRequired
category任意


GET /v1/api/news/movie/detail/
ニュース動画詳細を取得する。

ParameterRequired
movie_id必須
category任意

このAPIをRESTっぽく呼び出せるようにします。
以下のような感じで呼び出したいですよね!(個人の意見です)


API設計 改 ※実際に使用しているAPIとは(ry

エンドポイント
http://api.example.com/news/

IPアドレス
192.168.161.110

GET /news/
ニュース記事一覧を取得する。

ParameterRequired
category任意


GET /news/:id/
ニュース記事詳細を取得する。

ParameterRequired
category任意


GET /news/category/
カテゴリ一覧を取得する。

ParameterRequired
なし


GET /news/movie/
ニュース動画一覧を取得する。

ParameterRequired
category任意


GET /news/movie/:movie_id/
ニュース動画詳細を取得する。

ParameterRequired
category任意

一覧取得の/listを省略し、詳細取得の必須パラメータであるidをパスに指定する形式に変更しました。このような形式で呼び出せるようにNginxで実装します。


NginxでのRESTっぽくAPIを呼び出す実装

nginx.conf
upstream news-api {
server 192.168.161.100;
}

server {



location /news {
location = /news/ {
rewrite ^(.*)$ /v1/api/news/list/ break;
proxy_pass http://news-api;
}
location ~ ^/news/([^/]*)/?$ {
rewrite ^/news/([^/]*)/?$ /v1/api/news/article/?id=$1 break;
proxy_pass http://news-api;
}
location ^~ /news/category {
location = /news/category/ {
rewrite ^(.*)$ /v1/api/news/category/list/ break;
proxy_pass http://news-api;
}
}
location ^~ /news/movie {
location = /news/movie/ {
rewrite ^(.*)$ /v1/api/news/movie/list/ break;
proxy_pass http://news-api;
}
location ~ ^/news/movie/([^/]*)/?$ {
rewrite ^/news/movie/([^/]*)/?$ /v1/api/news/movie/detail/?movie_id=$1 break;
proxy_pass http://news-api;
}
}
}



}

気をつけることはlocationディレクティブの優先順位です。ニュース記事詳細(/news/:id/)よりカテゴリ一覧(/news/category/)を優先したいのでカテゴリ一覧を前方一致、記事詳細を正規表現で記述しています。今回作成したものはlocationの順不同で動作するようにしました。


APIのパスが多く冗長になる場合はlocation /news {}の中身をincludeして別ファイルにしておくと見やすくなると思います。

nginx.conf
upstream news-api {
server 192.168.161.100;
}

server {



location /news {
include /{$pj}/news.conf;
}



}

news.conf
location = /news/ {
rewrite ^(.*)$ /v1/api/news/list/ break;
proxy_pass http://news-api;
}
location ~ ^/news/([^/]*)/?$ {
rewrite ^/news/([^/]*)/?$ /v1/api/news/article/?id=$1 break;
proxy_pass http://news-api;
}
location ^~ /news/category {
location = /news/category/ {
rewrite ^(.*)$ /v1/api/news/category/list/ break;
proxy_pass http://news-api;
}
}
location ^~ /news/movie {
location = /news/movie/ {
rewrite ^(.*)$ /v1/api/news/movie/list/ break;
proxy_pass http://news-api;
}
location ~ ^/news/movie/([^/]*)/?$ {
rewrite ^/news/movie/([^/]*)/?$ /v1/api/news/movie/detail/?movie_id=$1 break;
proxy_pass http://news-api;
}
}

外部のAPIでAPI自体の変更ができないときも、この方法ならRESTっぽく呼び出せるようになるので、(公開APIにRESTでないAPI自体あまりないと思いますが)試してみてください。


明日のAdvent Calendarは、同じくニュースチームのエンジニアです。お楽しみに!

エンジニア募集

エキサイトではエンジニアとして一緒に働いてくださる方を
新卒採用中途採用で募集しています。

詳しくは、こちらの採用情報ページをご覧ください。


[PR]
by ex-engineer | 2016-12-08 09:00