WEB API The Good Parts 6章の自分的まとめ

今回で本シリーズは最後となる。
以下の本の6章を自分的にまとめる。

Web API: The Good Parts

Web API: The Good Parts

もしAPI を開発している、もしくはAPI を日々触っているのであれば、 是非買って読んでみてほしい。

堅牢なWeb API を作る

今回は重要なポイントは多いが、疑問や問題点はなかったので、 各章を一言でまとめて終わろうと思う。

  • どんなセキュリティの問題があるか
    以下の3つのパターンに分けて考える。
    • サーバとクライアントの間での情報の不正入手
    • サーバの脆弱性による情報の不正入手や改ざん
    • ブラウザからのアクセスを想定しているAPI における問題

サーバとクライアントの間での情報の不正入手

  • HTTPS にする。ただし100%安全であるとは言えないが、有効な手段である。

サーバの脆弱性による情報の不正入手や改ざん

  • XSS対策
    JSON をブラウザが必ずJSON と認識するようにする

    • Content-Type: application/json を指定する
    • IE8以上対策 → X-Content-Type-Optioins: nosniff を指定する
    • IE7以下対策 → 追加のリクエストヘッダをつけてそれを検証する or JSON をエスケープする

  • XSRF対策

    • データの更新にはPOST,PUT,PATCH,DELETE を用いる
    • XSRF トークンを使う。(そのサイトが発行したワンタイムトークン、もしくはセッションごとにユニークなトークン)

  • JSON ハイジャック
    JSON ハイジャックを防止するには、現在のところ以下の対策が有効。

    • JSON をSCRIPT 要素では読み込めないようにする
      → 追加のリクエストヘッダをつけて検証する

    • JSON をブラウザが必ずJSON と認識するようにする
      XSS対策と一緒

    • JSONJavaScript として解釈不可能、あるいは実行時にデータを読み込めないようにする
      JSON の配列ではなくオブジェクトで返す。もしくはJSON ファイルの先頭に無限ループなどを仕込んで読み込み時に処理が進まないようにする。

セキュリティ関係のHTTPヘッダ

X-Content-Type-Options

X-Content-Type-Options: nosniff

上記のヘッダをつけるだけで、勝手にメディアタイプを解釈されることを防ぐ

X-XSS-Protection

ブラウザがXSS の検出、防御機能を備えている。 IE8 以上では以下のヘッダを送ることで、設定を有効化できる。 他ブラウザでは上書きできない(デフォルト有効)

X-XSS-Protection: 1; mode=block

X-Frame-Options

フレーム(FRAMEとIFRAME 要素)内で読み込まれることを防いでくれる。

X-Frame-Options: deny

Content-Security-Policy

読み込んだHTML内のIMG 要素、SCRIPT 要素、LINK 要素などの読み込み先としてどこを許可するのかを指定するためのヘッダ。
API の場合は要素を指定することがないため、直接は効果がないかも。

Contet-Security-Policy: default-src 'none'

Strict-Transport-Security

あらかじめこのサイトはHTTPS でのアクセスが必須、ということを記録させておくためのヘッダ。

Strict-Transport-Security: max-age=15678000

max-age で指定された期間中は、同じホストへのアクセスはHTTPが指定されていても、HTTPSを使うようにする。

Public-Key-Pins

SSL 証明書が偽造されたものでないかチェックする。
使い方は以下を参照。

Public Key Pinning - Web セキュリティ | MDN

Set-Cookieヘッダとセキュリティ

セッション管理にクッキーを使用する場合にセキュリティを向上させるために、 Secure、HttpOnly 属性を付与する。

Set-Cookie: session=XXXX; Path=/; Secure; HttpOnly
Secure HttpOnly
HTTPSの通信しかクッキーを送らない設定 クッキーがHTTP の通信のみで使われ、JavaScriptなどのスクリプトではアクセスできない設定

大量アクセスへの対策

そのサービス特性に基づき、ユーザ、IPごとなどの基準を設けて最大アクセス回数(レートリミット)を決める。

制限値を超えてしまった場合の対応

  • HTTPステータス 429 を返す。
  • エラーの詳細をレスポンスに含める。
  • Retry-After ヘッダを使って次のリクエストをするまでにどれくらい待てばよいかを指定する

レートリミットをユーザに伝える

以下のHTTP ヘッダを独自に作成して使う。

ヘッダ名 説明
X-RateLimit-Limit 単位時間あたりのアクセス上限
X-RateLimit-Remaining アクセスできる残り回数
X-RateLimit-Reset アクセス回数がリセットされるタイミング

リーダブルコード ~より良いコードを書くためのシンプルで実践的なテクニック~ エンジニアなら読むべき本の一つ!

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

昨日、今日でこの本を読んだ。
非常に読みやすく、遅読の自分でもすぐに読み終わることができた。

書いてある内容は保守性を気にしている人やリーダブルコードを意識している人にとっては、 新しいものではないが、非常に重要なことを整理してくれる。

とくに自分としては「短いコードを書く」という章を読んではっとした。
正しくてきれいなコードを書いたとしても、 その機能が過多で使われないならリーダブルコードにはならないのだ。
そう!必要な機能を見極めて、簡単に(=短いコードで)実装をするのが良いのだ。

自分のために各章のタイトルだけ書いておく。
自分としての目標はタイトルを見て、自分がなにをすべきか分かるようになるまで繰り返し読むことだ。

  1. 理解しやすいコード
  2. 名前に情報を詰め込む
  3. 誤解されない名前
  4. 美しさ
  5. コメントすべきことを知る
  6. コメントは正確で簡潔に
  7. 制御フローを読みやすくする
  8. 巨大な式を分割する
  9. 変数と読みやすさ
  10. 無関係の下位問題を抽出する
  11. 一度に1つのことを
  12. コードに思いを込める
  13. 短いコードを書く
  14. テストと読みやすさ 15.「分/時間カウンタ」を設計・実装する

WEB API The Good Parts 5章の自分的まとめ

続きで、今回は以下の本の5章を自分的にまとめたいと思う。

Web API: The Good Parts

Web API: The Good Parts

もしAPI を開発している、もしくはAPI を日々触っているのであれば、 是非買って読んでみてほしい。

設計変更をしやすいWeb API を作る

今回は重要なポイントは多いが、疑問や問題点はなかったので、 各章を一言でまとめて終わろうと思う。

  • 設計変更のしやすさの重要性

    • 外部に公開しているAPIの場合
      利用者が多く、利用者がきちんと対応できるようにするなどAPI の変更は超大変!
    • バイルアプリケーション向けAPI の場合
      外部公開よりは楽だが、それでもOSのバージョンがあげられないなどAPI の変更は結構大変!
    • ウェブサービス上で使っているAPI の場合
      ウェブも自分のものなので楽だが、それでもキャッシュされているなどちょっと大変!

      そうじて「一度公開をしたWeb APIの仕様を変更するのはいずれにせよ問題が発生する危険性がある」ということ。

  • API をバージョンで管理する
    以下のパターンがあるが、一般的に使われるのはURIにバージョンを埋め込む方法。

    • URI にバージョンを埋め込む
    • バージョンをクエリ文字列を入れる
    • メディアタイプでバージョンを指定する

  • バージョン番号をどうつけるか
    以下が一般的。

メジャーバージョン マイナーバージョン パッチバージョン
後方互換性のない変更が行われた場合 後方互換性のある機能変更、あるいは特定の機能が今後廃止されることが決まった場合 ソフトウェアのAPI に変更がないバグ修正などを行った場合


  • (メジャー)バージョンを変える際の指針
    後方互換性を保つことが可能な変更は可能な限り、同じバージョンでのマイナーバージョンアップで対応し、 バージョンをあげるのはどうしても後方互換性を保ったまま修正を行うことが難しい変更を加えなければならないときのみ、メジャーバージョンをあげる。

  • API の提供を終了する
    Twitter の例のように以下の対応が必要!

    • 新しいAPI の告知
    • API を廃止するという継続的な告知
    • Blackout Test と呼ばれる一時的に旧API を停止してアクセスできないようにするテスト

  • あらかじめ提供終了時の仕様を盛り込んでおく
    提供終了した場合に例えば410エラー(Gone)を返す、など提供終了時の仕様を決めておく。

  • 利用規約にサポート期限を明記する
    一定期間はAPI のバージョンをサポートする、と名言する。 サービスの目的により、保証とサービス提供の範囲の明確化が重要な場合は有効。

  • オーケストレーション
    汎用的なAPI はどうしても使い方が煩雑になる。 例えば1つのアクションを行うのに複数のAPI にアクセスしなければならなかったり、不要なデータを受け取らなければならず ペイロードが大きくなってしまう。

    サーバ側の汎用的なAPI とクライアントの間に"Client Adapter Code"を実行するオーケストレーション層をはさんで、 さまざまなデバイスに対応できるようにする。

    このオーケストレーション層を作成するのはクライアント側のエンジニアで、 クライアント側のデバイス機能やリリースサイクルに合わせて、エンドポイントを修正できる。

    Netflix のブログ記事 The Netflix Tech Blog: The Netflix Dynamic Scripting Platform によれば、
    しっかりとしたデバイス開発者用エンドポイント管理ツールが提供されており、 いわば社内専用PaaS として提供されているらしい。

さすがNetflix、はうらやましい限りですね!

WEB API The Good Parts 4章の自分的まとめ

前々回からの続きで、今回は以下の本の4章を自分的にまとめたいと思う。

Web API: The Good Parts

Web API: The Good Parts

もしAPI を開発している、もしくはAPI を日々触っているのであれば、 是非買って読んでみてほしい。

HTTP の仕様を最大限利用する

4.2 ステータスコードを正しく使う

この章についてはHTTP のステータスコードを解説しているが、 内容はwiki を見るだけで十分。

HTTPステータスコード - Wikipedia

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

JSONXML を用いた新しいデータ形式を定義する場合は+ で記載する。
例えば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: jaAccept-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 認証が表示され、アクセスできない。。。

f:id:AHA_oretama:20160710164704j:plain

なんてこった。。。

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();
    }

}

@EnableWebSecurityWebSecurityConfigurer を設定するときのアノテーションで、 WebSecurityConfigurerAdapterconfigure(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 にして返却してくれる。

サンプル

いつものように作成したリポジトリを公開しているので、参考にしてほしい。

github.com

WEB API The Good Parts 3章の自分的まとめ

前回からの続きで、今回は以下の本の3章を自分的にまとめたいと思う。

Web API: The Good Parts

Web API: The Good Parts

3. レスポンスデータの設計

各サブの章を一言でまとめられた。
エラー部分については重要だなぁ、と感じたため、しっかり解説。

  • データフォーマット
    JSONをサポートすべき。それ以外(xml、MessagePackなど)はオプション。
    データフォーマットの指定方法は以下の3つ。記号はおすすめ度。

    • ○ クエリパラメータ
    • × 拡張子
    • ○ Acceptヘッダ

  • レスポンスの内容をユーザが選べるようにする
    そのままの意味。選べる値はフィールドごとか、グループ単位かのいずれかで、その情報をクエリパラメータに付与する。

http://api.example.com/v1/users/12345?fields=name,age
  • エンベロープは必要か
    メタデータを含んだ形ですべてのAPIが同じデータ構造を返すために実際のデータをくるむための構造をエンベロープと呼ぶ。
    HTTPヘッダがメタ情報を持っているため、エンベロープは不要。

  • データはフラットにすべきか
    不要な階層化はするべきではないが、階層化すべきものはする。
    たぶん開発時は普通にオブジェクトをつくれば、それで階層化すべきものはされ、不要に階層化していない形になると思う。

  • 配列とフォーマット
    トップレベルに配列を包んだオブジェクトを置くか、直接配列を置くか。
    筆者としてはオブジェクトで包んだほう推し。理由は以下のよう。

    • レスポンスデータが何を示しているものかがわかりやすくなる
    • レスポンスデータをオブジェクトに統一することができる
    • JSONインジェクションを防ぎ、セキュリティ上のリスクを避けることができる

  • 各データの名前
    前回と同じ考え方。

    • 多くのAPIで同じ意味に利用されている一般的な単語を持ちいる
    • なるべく少ない単語数で表現する
    • 複数の単語を連結する場合、その連結方法はAPI 全体を通して統一する
    • 変な省略形は極力利用しない
    • 単数形/複数形に気を付ける

  • 大きな整数とJSON
    32bit を超える数値は丸め込み誤差が発生するため、文字列を使うべき。

3.6 エラーの表現

3.6.1 ステータスコードでエラーを表現する

HTTP のステータスコードを正しく使って表現すること。

ステータスコード 意味
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアントサイドに起因するエラー
500番台 サーバサイドに起因するエラー

3.6.2 エラーの詳細をクライアントに返す

上記のステータスコードだけではわからないので、レスポンスボディにエラー専用のデータ構造を用意して、エラーの詳細を返す。

{
  "error" : {
    "code": 2013,
    "message": "Error Description",
    "info": "Detail Description or its url"
  }
}

3.6.4 エラーの際にHTMLが返ることを防ぐ

例えば存在しないURL を叩いた場合にWEBサーバ側の設定でHTML が返ってしまうことがないようにしっかり設定する。
つまり、API だけでなく、その周りのミドルウェアの設定も必要。

3.6.5 メンテナンスとステータスコード

メンテナンスでAPI を停止する場合は503 のHTTPステータスコードを返却し、Retry-After のヘッダで再開の時刻を知らせるべき。

じゃあどうやって実装するの?

レスポンスの内容をユーザが選べるようにする

今回の部分であれ?どうやって実装するんだろう?って思ったのは、
とくに「レスポンスの内容をユーザが選べるようにする」部分だ。

実装イメージがわかなかったので調べたみた。
実際に実装するのはJava、Spring Boot。

こういうのはやっぱりStack OverFlow に書いてあるね!
参考にしたのは以下。

json - Spring rest api filter fields in the response - Stack Overflow

Model クラスの作成

ここではlombok を使用している。

@JsonFilter(RestControllerAdvice.PROFILE_FILTER)
@Data
public class Profile {

    private String firstName;
    private String lastName;
    private LocalDate birthDay;
    private String gender;
}

ここで重要なのはJsonFilter アノテーションを付与していること!
これでfilter 対象であることを表している。

Controller クラスの作成

Profile クラスを作成して、値を全部つめて返却する簡単なController クラス。

@RestController
public class ProfileController {

    @RequestMapping(value = "/api/v1/users/me/profile", method = RequestMethod.GET)
    public Profile getV1Profile() {
        Profile profile = new Profile();
        profile.setFirstName("太郎");
        profile.setLastName("山田");
        profile.setBirthDay(LocalDate.of(1986, 1, 1));
        profile.setGender("MALE");

        return profile;
    }
}

Advice クラスの作成

リクエストに詰められたfields は一括してAdviceクラスで処理するようにする。

@ControllerAdvice(annotations = RestController.class)
public class RestControllerFilterAdvice extends AbstractMappingJacksonResponseBodyAdvice {

    public static final String PROFILE_FILTER = "PROFILE_FILTER";

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
            MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {

        // fields パラメータの取得
        String fieldsParamter = ((ServletServerHttpRequest) request).getServletRequest().getParameter("fields");

        String[] fields = fieldsParamter == null ? new String[0] : fieldsParamter.split(",");
        // Filter の設定
        FilterProvider filters = new SimpleFilterProvider().addFilter(PROFILE_FILTER,
                fields.length == 0 ? SimpleBeanPropertyFilter.serializeAllExcept(fields)
                        : SimpleBeanPropertyFilter.filterOutAllExcept(fields));
        bodyContainer.setFilters(filters);
    }
}

まずAbstractMappingJacksonResponseBodyAdvice クラスをextends する。
このクラスを継承することでレスポンスボディの変更が楽に行える。
beforeBodyWriteInternal メソッドだけコーディングすればよくなるのだ!

レスポンスのフィールドを指定するのはFilterProvider クラスを作成して、 MappingJacksonValue のフィルターに指定すればよい。

リクエストにfieldsパラメータがあるかないかでfilter のメソッドを変更している。
これはパラメータがない場合には、全フィールドを返却するためである。

GitHub

今回作ったものはGitHub にあげておくので、参考にしてくださいねー。

github.com

所感

今回学んだ、こうあるべきって内容については普通に身についていた。

一つどうやって実装するんだろうってイメージがつかめない部分があったが、
実際にコーディングしてみて意外と簡単に実装できるものだなぁ、と感じた。

重要なのは概念を理解すること、そして実装イメージをもつこと!

Web API The Good Parts 1~2章の自分的まとめ

自分は本を読むのが遅いほうだが、最近は読んだ後に自分の中にあまり内容が残らないことが多くなってきた。。。
なので、これからは読んだ本があれば、随時自分の中でまとめたことをアウトプットとしてまとめていきたい。

今回、読んでいる本はこちら!

Web API: The Good Parts

Web API: The Good Parts

1. Web API とは何か

1.1 WEB API の重要性

まあつまりAPIを開発していたら公開したほうがよいということ。
利用者がサービスに付加価値を与えてくれ、コアとなるサービスがより発展するから。

ほんとにその通り。
ただここでは触れていないが、公開するならセキュリティとか気になるところ…
6章で紹介されるので一旦、おあずけw

1.3 何をAPI で公開すべきか

サービスのコアの価値のある部分をすべて利用可能なように公開すべき。
そこに価値があるため。

公開したAPIの運用の方法はいくつかある。
多くはレートリミット、つまりアクセス数の制限を設けている。
それ以上のアクセスがあれば有料とするなどの運用もあり。
レートリミットについても6章で紹介されるので一旦、おあずけw

API を公開することで、優先順位をさげていた機能や全く思いつきもしなかった新しい機能を誰かが作ってくれるかもしれないという効果がある。
このように、(直接サービスを使うのではなく、)API を提供し、それを複数組み合わせたアプリケーションがユーザにサービスを行う”間接販売”モデルになっていく。

1.4 Web API を美しく設計する重要性

設計の美しいWeb API

  • 使いやすい
  • 変更しやすい
  • 頑強である
  • 恥ずかしくない

当たり前だけど超重要!
この章はしっかり読んだ置いたほうがよい!

1.5 Web API を美しくするには

そのためには

デファクトスタンダードを決める際に参考になるサイトはProgrammableWeb。

http://www.programmableweb.com/

(世界中のAPI が集められているサイト。
なんかアプリ作ろうとしたらここからAPI を選定するといいかも)

1.7 対象となる開発者の数とAPI の設計思想

  • LSUDs(large set of unkown developers)
    対象が不特定多数のため、さまざまなユースケースを想定して汎用的にすべき。
  • SSKDs(small set of kown developers)
    特定の開発者にとって便利で使いやすくすべき。

2.エンドポイントの設計とリクエストの形式

2.2 API エンドポイントの考え方

覚えやすく、どんな機能をもつURI なのかが一目でわかるのがよい。
そのためには

  • 短く入力しやすいURI
  • 人間が読んで理解できるURI
    • むやみに省略しない
    • 正しい英語を使う
  • 大文字小文字が混在していないURI
  • 改造しやすいURI
  • サーバ側のアーキテクチャが反映されていないURI
  • ルールが統一されたURI

自分の情報のエイリアス

idではなく、me, self を使う。

2.3 HTTP メソッドとエンドポイント

正しくHTTPメソッドを使いましょう!

メソッド 用途
GET 情報の取得
POST 情報の登録
PUT 情報の(全体)更新
DELETE 削除
PATCH 情報の一部更新

get,postメソッドしか使えない環境のために、X-Http-Method-Override を使えるようにしておくべき。

2.4 API のエンドポイント設計

リソースをURIにする。

ex.ユーザ情報

 https://api.example.com/v1/users
 https://api.example.com/v1/users/:id

それ以外の注意点。

  • 複数形の名詞を利用する
  • 利用する単語に気をつける
  • スペースやエンコードを必要とする文字を使わない
  • 単語をつなげる必要がある場合はハイフンを利用する

2.5 検索とクエリパラメータの設計

クエリパラメータで絞込みをする。

 https://api.example.com/v1/users?name=ken

クエリパラメータとパスの使い分けは以下の2観点。

  • 一意なリソースを表すのに必要な情報かどうか
  • 省略可能かどうか

ページネーションはper_page/page or limit/offsetのいずれかを推奨。
page は1始まり、offsetは0始まり。

2.6 ログインとOAuth2.0

OAuth は認可を行う。
例えばFacebook の情報を自サービスで使う場合、以下の図のようにトークンのやり取りを行い、認可をもらう。
重要なのはconsumers(ここでは自サービス)ではログイン情報(ID,パスワード)は持たず、
ユーザはservice Providers(ここではFacebook)にログインするだけでよく、
その際にconsumers にどのような権限を与えるか制御できる。

f:id:AHA_oretama:20160627013911j:plain

自サービスでログインを行う場合、OAuth2.0 のGrant Type=Resource Owner Password Credentials で実装する。

参考

Grant Type 用途
Authorization Code サーバサイドで多くの処理を行うウェブアプリケーション向け
Implicit クライアントサイドで多くの処理を行うアプリケーション向け
Resource Owner Password Credentials consumers を利用しないアプリケーション向け
Client Credentials ユーザ単位での認可を行わないアプリケーション向け

ここら辺は実際に実装してみないとよくわからない。

2.9 HATEOAS とREST LEVEL3 API

HATEOAS
hypermedia as the engine of application state
API の返すデータの中に、次に行う行動、取得するデータなどのURI をリンクとして含めること。

REST LEVEL3 API
HATEOAS の概念の導入。
LEVEL0~2はそれぞれHTTP、リソース、メソッドの導入。

Spring でもプロジェクトがあるので、使ってみるといいかもしれない。
http://projects.spring.io/spring-hateoas/

ここまでの所感

API 設計の基礎の基礎となる部分であり、
今まで使っていたAPI がここで学んだAPI の基本設計を自然と取り入れられていたことに気づいた。

今まで自然と使っていたが、気づいていなかったことが多かったので非常に勉強になった。
そういう意味だけでもAPI 開発者としては読んでおくべき本かな、と思った。

また仕事で作っているAPI もこの基本設計は守っていつつ、
足りない部分もあったので反映していきたい。

シェルスクリプトの本を読んでシェルを書いてみた

シェルスクリプト 基本リファレンス」の本について

今回、読んでみた本は以下の「シェルスクリプト 基本リファレンス」だ。

[改訂新版] シェルスクリプト基本リファレンス  ??#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

[改訂新版] シェルスクリプト基本リファレンス  ??#!/bin/shで、ここまでできる (WEB+DB PRESS plus)

総評

まず対象としてる読者はLinux で基本的なコマンド(ls, cp, mv など)を使うことができる人となっているが、 一から説明してるので初心者でも大丈夫だ。

内容としてはBシェル系、Cシェル系の違いや「#!/bin/sh」の説明など基本的なことから、 シェルの文法、シェルのコマンドなど基礎的な事柄をしっかり説明している。 ノウハウなど一部、実践的なことも書いてある。

総評としてはシェルを書き慣れていない人にはオススメの一冊である! 是非とも読んでみてほしい。

シェルスクリプト解説

#!/bin/sh, #!/bin/bash の意味

正直、シェルスクリプトの一行目に「#!/bin/sh」を書くのは慣例だと思っていた。 が、違った!ちゃんと意味があった。

単純に言えばシェルスクリプトに何を使うか、だ。

  #!/bin/sh

と書くと、Bourne Shell が使われる。 「#!/bin/bash」ならBourne-Again shell だ。bash のほうがsh より高機能。

では、ターミナルを開いたときにどのシェルと使ってるかは以下のコマンドで調べられる。

  echo $SHELL

パラメータ展開

パラメータは$をつけることで表される。 主なところはこんな感じだ。

書き方 説明
"{$HOME}" 変数の値を参照
"$1","$2",.. シェルスクリプト、シェル関数の引数を参照
$0 シェルスクリプト名を参照
"$@" シェルクリプト、シェル関数の引数全てを参照
$# シェルスクリプトの引数の個数
$? 直前のコマンドの終了ステータスを参照

ついでにダブルクォートで囲んでいるのは パラメータ展開をしたあとの文字列をパラメータ展開などを行わずに 文字列として展開することを示している。

例えば

  file=*
  echo $file
  # 現在のディレクトリの全ファイル、フォルダが表示
  echo "$file"
  # * が表示

のように、ダブルクォートで参照した文字列を再度、展開することを避けてくれる。

$#,$?などは数字の文字列が返ってきて、パラメータ展開などされる心配がないので、 ダブルクォートで囲んでいない。

シングルクォート、ダブルクォート、バックスラッシュ、バッククォートの違い

正直これはこの本を読むまで全く分からなかった。 これが分かっただけでもこの本を読む価値がある。

ここでは簡単なまとめを表にする。

パラメータ展開 コマンド置換 説明
シングルクォート × × 文字通りの意味として解釈される
ダブルクォート ○(``で囲む) $,`,\ の特殊文字が解釈される
バックスラッシュ - - 次の文字が特殊な意味を失う
バッククォート × 囲まれた部分がコマンドとして実行される
  echo '$HOME'
  # $HOME
  echo "$HOME"
  # /home/aha_oretama
  echo \$0
  # $0
  echo `expr 1 + 1`
  # 2

役立つテンプレート

他の人のシェルを見てると使い方、初期チェックなど お決まりの定型句があるように見えるが、 その定型句が人によってバラバラだったので全然覚えられなかった。

ここでは自分が一番良いと思った定型句を書く。

#!/bin/sh ← 何のシェルスクリプトを使うかを指定

# 使い方を指定
usage () {

cat <<EOT
Usage:
  "$0" [-o outputPath] targetFile
Description:
  Unzip file.
  File extension includes .tar .tar.gz .tar.Z .tar.bz2 .zip 
Options:
  -o  output-directory
EOT
## ヒアドキュメントで記載
## <<HOGEで始まり、次にHOGEが出るまでヒアドキュメントとなる

exit 1
}

# オプションの設定
# o: のようにコロンはオプションに引数があることを示す
while getopts ho: option
do 
  case $option in
    o)
      OUTPUT_PATH="$OPTARG"
      ;;
    h)
      usage
      ;;
    \?)
      usage
      ;;
  esac
done

# オプションを除く
shift `expr "$OPTIND" - 1`

# オプション以外の引数を確認
if [ $# -lt 1 ]; then
  usage
fi

最後に

この本はシェルスクリプトを学びたい人にとっては 基礎を固める最良の本となる。

この本を読んで作った簡単なUtil シェルを GitHub にあげたのでぜひ参考にしてほしい。

github.com