WEB API The Good Parts 4章の自分的まとめ
前々回からの続きで、今回は以下の本の4章を自分的にまとめたいと思う。
- 作者: 水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/11/21
- メディア: 大型本
- この商品を含むブログ (7件) を見る
もしAPI を開発している、もしくはAPI を日々触っているのであれば、 是非買って読んでみてほしい。
HTTP の仕様を最大限利用する
4.2 ステータスコードを正しく使う
この章についてはHTTP のステータスコードを解説しているが、 内容はwiki を見るだけで十分。
4.4 メディアタイプの指定
メディアタイプとはデータ本体の形式を表すもので、
リクエスト時にはレスポンスがこの形式でほしいと指定する場合はAccecpt
ヘッダー、
リクエスト、レスポンス時に送信するデータ本体はこの形式であると指定する場合はContent-Type
ヘッダーで表す。
代表的なメディアタイプは以下の表を参照する。
メディアタイプ | データ形式 |
---|---|
text/plain | プレーンテキスト |
text/html | HTML 文書 |
application/xml | XML 文書 |
text/css | CSS 文書 |
application/javascript | JavaScript |
application/json | JSON 文書 |
application/rss+xml | RSS フィード |
application/atom+xml | Atom フィード |
application/octet-stream | バイナリデータ |
application/zip | zip ファイル |
image/jpeg | JPEG 画像 |
image/png | PNG 画像 |
image/svg+xml | SVG 画像 |
multipart/form-data | 複数のデータで構成されるウェブフォームデータ |
video/mp4 | MP4 動画ファイル |
application/vnd.ms-exel | Excel ファイル |
application/x-msgpack | MessagePage |
application/x-yaml | YAML |
application/x-plist | プロパティリスト |
application/x-www-form-urlencoded | HTMLのフォームデータ |
トップレベルのapplication, text が混同しやすいが、
appllication のほうが主流。
plain, html ,css 以外はapplication となる。
サブタイプがx-
で始まるものはIANA に登録されていない、
もしくはもしくは過去登録されていなかった歴史的経緯が残っているもの。
自分で作る場合は以下の表を参考にするとよい。
いまはあまりx-
は推奨されないよう。。。だが一般的にはx-
を付与している。
ツリー名 | 接頭辞 |
---|---|
Standards tree(標準ツリー) | なし |
Vendor tree(ベンダツリー) | vnd |
Personal tree(パーソナルツリー) | prs. |
Unregistered tree(未登録ツリー) | x. |
vnd.ms-excel
はマイクロソフトというベンダーのexcel という意味。
ただし、通常は以下のように会社名を入れるらしい。
application/vnd.companyname.awesomeformat
JSON やXML を用いた新しいデータ形式を定義する場合は+
で記載する。
例えばXML をベースとしたRSSフィードの場合は以下のようになっている。
application/rss+xml
4.3 キャッシュとHTTP の仕様
キャッシュタイプ | タイプ説明 | 対応ヘッダ | 説明 |
---|---|---|---|
Expiration Model(期限切れモデル) | あらかじめレスポンスデータに保存期間を決めておき、期限が切れたら再度アクセスをして取得する | Expires: Fri, 01 Jan 2016 00:00:00 GMT |
指定した時間以降はキャッシュ切れと判断する |
Cache-Control: max-age=3600 |
指定した秒数が経過した以降はキャッシュ切れと判断する | ||
Validation Model(検証モデル) | いま保持しているキャッシュが最新であるかを問い合わせて、データが更新されていた場合に取得する | response:Last-Modified: Tue, 01 Jul 2014 00:00:00 GMT → request:If-Modified-Since:Tue, 01 Jul 2014 00:00:00 GMT |
最終更新時刻を記述する。時刻が一致すれば最新キャッシュと判断する。 |
response:ETag: "1234567890123456" → request:If-None-Match:"1234567890123456" |
任意の文字列。通常はハッシュ値など。値が一致すれば最新キャッシュと判断する。 | ||
キャッシュさせない | キャッシュを全くさせたくない場合に用いる | Cache-Control: no-cache |
キャッシュしない |
キャッシュを行う際に、URI 以外にどのリクエストヘッダ項目をデータを一意に特定するために利用するか特定するために、Vary
ヘッダを指定する。
例えば、Accept-Language: ja
とAccept-Language: en
を指定して送信されたデータは異なるものになるため、
Vary: Accept-Language
と指定する。
4.5 同一生成元ポリシーとクロスオリジンリソース共有
XHTTPRequest では異なるドメインに対してアクセスを行い、レスポンスデータを読み込むことができない。 これは同一生成元ポリシー(Same Origin Policy)というセキュリティ上のポリシーによるものである。
これを回避するために、クロスオリジンリソース共有(CORS:Cross-Origin Resource Sharing)がある。
実装方法はSpring-Bootならここを参考にしてほしい。
Getting Started · Enabling Cross Origin Requests for a RESTful Web Service
実装ができればこのような動きになる。
リクエストヘッダ | サーバ処理 | レスポン |
---|---|---|
Originヘッダに送信元のドメイン | 許可する一覧かチェックする | 許可されている場合:Access-Control-Allow-Originヘッダに許可されたドメイン |
許可されてない場合:403 エラー |
CORS にはプリフライトリクエストというものがあり、 これはリクエストを行う前にそのリクエストが受け入れられるかどうかを事前にチェックするもので、 OPTIONS メソッドを使ってリクエストすると、許可されているメソッドやヘッダーが表示される。
キャッシュヘッダの実装
今回、全然使ったことがないヘッダはキャッシュのところ。
なので、自分で実装を試してみた。
環境
- Java 1.8
- Spring Boot 1.3.6
そもそもヘッダー関連の実装って?
そう、レスポンスに毎回同じ設定をするのって大変だなぁ。。。
Spring-Boot でカバーしてないかなぁ?って思ったら、カバーしてました!
Spring Security |
---|
Spring Security Reference |
さっそくpom.xml
に追加してみる。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>
ここからはSpring Security 初心者の私につきあってもらいたい。。。
適当にAPI を作成してアクセスしてみると、Basic 認証が表示され、アクセスできない。。。
なんてこった。。。
Spring Security のページを見ると大半がAuthentication, Authorization とかについて書かれている。
デフォルトでBasic 認証が有効になってしまうのか。。。
一つ勉強になったところで、Basic 認証を無効にしてみる。
以下のクラスを追加することで、Basic 認証していたコードを上書きし、 全てのリクエストを許可するようになる。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll(); } }
@EnableWebSecurity
がWebSecurityConfigurer
を設定するときのアノテーションで、
WebSecurityConfigurerAdapter
のconfigure(HttpSecurity http)
がデフォルトでBasic 認証をしているコードとなるので、
それをOverride
して上書きする。
ここではBasic 認証をしている親クラスのメソッドsuper.configure(http)
を呼び出してはいけない。
Sping Security を導入したことで、レスポンスヘッダーが以下のようになって、 API のデフォルトのキャッシュは無効になっている。
Cache-Control:no-cache, no-store, max-age=0, must-revalidate Content-Type:application/json;charset=UTF-8 Date:Sun, 10 Jul 2016 08:00:56 GMT Expires:0 Pragma:no-cache Server:Apache-Coyote/1.1 Transfer-Encoding:chunked X-Content-Type-Options:nosniff X-Frame-Options:DENY X-XSS-Protection:1; mode=block
Pragma:no-cache
は古いヘッダーのようで、Cache-Control
が効かないときの予備だそう。
それ以外にもXSS対策などもついている。
では、URI ごとのヘッダーの設定は?
まずは独自のヘッダーを記述するStaticHeadersWriter
を継承したクラスを作成する。
public class CacheControlHeadersWriter extends StaticHeadersWriter { /** * Creates a new instance */ public CacheControlHeadersWriter() { super(createHeaders()); } private static List<Header> createHeaders() { List<Header> headers = new ArrayList<Header>(); headers.add(new Header("Cache-Control", "Cache-Control: max-age=3600")); headers.add(new Header("Pragma", "cache")); return headers; } }
ここではmax-age
を使用して1日、キャッシュを保持させるようにしている。
さきほどのSecurityConfig
クラスに対象のURIパスを指定して、
一度キャッシュを無効にしてから上記のCacheControlHeadersWriter
を追加する。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll(); CacheControlHeadersWriter headersWriter = new CacheControlHeadersWriter(); http.antMatcher("/api/v1/prefectures/**").headers().cacheControl().disable().addHeaderWriter(headersWriter); } }
これで特定のURI のときだけキャッシュが効くようになる。
ではではChrome で確かめてみよう!! あれ?効かない。。。
調べたらなんと!同じタブで直接アクセスするとキャッシュは効かないという!!
iis - Is Chrome ignoring Control-Cache: max-age? - Stack Overflow
別タブなら効くというなんということだ!
親切すぎて、逆にはまりやすいポイントになった!
たしかに別タブならキャッシュが効く。よかった。。。
RestTemplate によるキャッシュは?
RestTemplate
はSpirng でAPI へリクエストを送信するためのクラスである。
このクラスでキャッシュを判別してキャッシュが有効であればそのキャッシュを利用する、ということをしたい。
しかし、どうやらまだ未対応なようだ。。。
[SPR-5821] HTTP cache and conditional requests support in RestTemplate - Spring JIRA
ただ他のGitHub に対応するためのMaven があるようだ。
GitHub - jirutka/spring-http-client-cache: A very simple HTTP cache for the Spring’s RestTemplate.
今回は上記は使用しないが、今後のSpring-Bootの開発に期待している。
【2016/08/02追記】Validation Model(検証モデル)
検証モデルの場合は簡単でSpring にその機能が提供されている
@RequestMapping(value = "/get", method = RequestMethod.GET) public String myHandleMethod(WebRequest webRequest, Model model) { long lastModified = // application-specific calculation if (request.checkNotModified(lastModified)) { // shortcut exit - no further processing necessary return null; } // further request processing, actually building content model.addAttribute(...); return "myViewName"; }
上記のようにcheckNotModified
を呼ぶだけで、
更新があった場合はLast-Modified
ヘッダーをつけてくれ、
更新がなかった場合はHTTPステータスを302 にして返却してくれる。
サンプル
いつものように作成したリポジトリを公開しているので、参考にしてほしい。