WEB API The Good Parts 3章の自分的まとめ
前回からの続きで、今回は以下の本の3章を自分的にまとめたいと思う。
- 作者: 水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/11/21
- メディア: 大型本
- この商品を含むブログ (7件) を見る
3. レスポンスデータの設計
各サブの章を一言でまとめられた。
エラー部分については重要だなぁ、と感じたため、しっかり解説。
データフォーマット
→ JSONをサポートすべき。それ以外(xml、MessagePackなど)はオプション。
データフォーマットの指定方法は以下の3つ。記号はおすすめ度。- ○ クエリパラメータ
- × 拡張子
- ○ Acceptヘッダ
レスポンスの内容をユーザが選べるようにする
そのままの意味。選べる値はフィールドごとか、グループ単位かのいずれかで、その情報をクエリパラメータに付与する。
http://api.example.com/v1/users/12345?fields=name,age
エンベロープは必要か
メタデータを含んだ形ですべてのAPIが同じデータ構造を返すために実際のデータをくるむための構造をエンベロープと呼ぶ。
HTTPヘッダがメタ情報を持っているため、エンベロープは不要。データはフラットにすべきか
不要な階層化はするべきではないが、階層化すべきものはする。
たぶん開発時は普通にオブジェクトをつくれば、それで階層化すべきものはされ、不要に階層化していない形になると思う。配列とフォーマット
トップレベルに配列を包んだオブジェクトを置くか、直接配列を置くか。
筆者としてはオブジェクトで包んだほう推し。理由は以下のよう。- レスポンスデータが何を示しているものかがわかりやすくなる
- レスポンスデータをオブジェクトに統一することができる
- JSONインジェクションを防ぎ、セキュリティ上のリスクを避けることができる
各データの名前
前回と同じ考え方。大きな整数と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 にあげておくので、参考にしてくださいねー。
所感
今回学んだ、こうあるべきって内容については普通に身についていた。
一つどうやって実装するんだろうってイメージがつかめない部分があったが、
実際にコーディングしてみて意外と簡単に実装できるものだなぁ、と感じた。
重要なのは概念を理解すること、そして実装イメージをもつこと!
Web API The Good Parts 1~2章の自分的まとめ
自分は本を読むのが遅いほうだが、最近は読んだ後に自分の中にあまり内容が残らないことが多くなってきた。。。
なので、これからは読んだ本があれば、随時自分の中でまとめたことをアウトプットとしてまとめていきたい。
今回、読んでいる本はこちら!
- 作者: 水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/11/21
- メディア: 大型本
- この商品を含むブログ (7件) を見る
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 にどのような権限を与えるか制御できる。
自サービスでログインを行う場合、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)
- 作者: 山森丈範
- 出版社/メーカー: 技術評論社
- 発売日: 2011/04/27
- メディア: 単行本(ソフトカバー)
- 購入: 9人 クリック: 119回
- この商品を含むブログ (9件) を見る
総評
まず対象としてる読者は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 にあげたのでぜひ参考にしてほしい。
Oracle XE processes、SPFILE の設定
Oracleでは、けっこうprocesses数が問題になることが多いので、ここでやり方をまとめておきたい。
環境
- Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
processes
ここではprocessesの変更を行うため、以下の権限を持ったユーザで実行する。
- ALTER SYSTEM
確認
select * from v$parameter where name = 'processes';
SPFILE の設定
案の定、上記を実行したときにエラーが発生した。
SPFILE でOracle XE を起動していなかったのだ…
ここではSPFILE の作成、設定も追記する。
SPFILEの作成のためにSYSDBA,SYSOPER のどちらかの権限のユーザで実行する。
SPFILE 確認
select * from v$parameter where name = 'spfile';
Value に値があればSPFILE で起動している。逆にnull であればPFILE で起動していることになる。
SPFILE 作成
CREATE SPFILE [= filePath] FROM PFILE [= filePath];
もし必要であればfilePath を指定することで、SPFILE の格納場所、PFILE の参照先を指定できる。
Groovy on Grails をインストールして使ってみた
会社でGroovyを使っているので、勉強していたらGrailsというものがあり、
GrailsならWebアプリケーションを簡単に作れるっぽいのでインストールしてみた。
インストール
Grails
Grailsは以下からインストールできる。
The Grails Framework
Posh-GVM (POwerSHell Groovy enVironment Manager) というGroovy の環境ツールがあるので、
せっかくなのでPosh-GVM を使ってインストールしてみた。
まずはPosh-GVM のインストール。
管理者権限のPowerShell で以下を実行。
- (new-object Net.WebClient).DownloadString('https://raw.githubusercontent.com/flofreud/posh-gvm/master/GetPoshGvm.ps1') | iex
- Import-Module posh-gvm
- gvm help
以下の画面が表示されればPosh-GVM のインストールは完了。
途中、PowerShell の実行ポリシーに引っかかった場合、以下をPowerShell 上で実行すれば解決した。
Set-ExecutionPolicy RemoteSigned
それではPosh-GVM を使用してGrails をインストールしてみよう。
といっても、以下コマンドをPowerShell で実行するだけだ。
gvm install grails
GGTS
Groovy/Grails Tool Suite というEclipse のGroovy/Grails 版があるのでインストールしてみた。
といっても、以下からダウンロードしてインストールするだけだ。
Groovy/Grails Tool Suite™ (GGTS)
インストールが終わってGGTS を開いてみるとなにかあやしい予感が…
おそるおそる「Preferences」→「Groovy」→「Grails」を見てみると…
GGTS に含まれてるやないか!!Grails !!
せっかくインストールしたのに…!!
しかもバージョンが違ってる…
しょうがないのでPosh-GVM を使用してGrails をバージョンして入れ直し。
gvm install grails 2.4.4
Grails コマンドを使用して「Hello,World!」
せっかく入れたGrails だけをつかって「Hello,World!」アプリケーションを作る。
PowerShell で以下のコマンドを順に実行する。
- grails create-app HelloWorld
- cd .\HelloWorld
- grails create-controller HelloWorld
- エディタで .\grails-app\controllers\helloworld\HelloWorldController.groovy を開き、編集する
package helloworld class HelloWorldController { def index() { render "Hello,World!" } }
5. grails run-app
うん、なんか大丈夫そうだな。
アプリケーションが起動したというメッセージが出た。
GGTS を使用して「Hello,World!」
先ほど作ったプロジェクトを「Grails Project」としてインポートし、
あとはRUNするだけでOK!
これでうまくいくと思ったらエラーがバンバン出た…エラーが止まらない!
java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor58.invoke(Unknown Source) … Caused by: java.lang.IllegalArgumentException: Can not copy a non-root Method at java.lang.reflect.Method.copy(Method.java:151) ... 390 more
さっそく調べてみると、ここに書いてあった。
Incompatible JVM in GGTS (Eclipse) and JAVA 1.8 - Stack Overflow
JDKのバージョンが対応していないとのこと。
JDK:1.8.0_60 をインストールして実行してみると成功した。
Java8から追加されたラムダ式についてまとめてみた。
Java8から追加されたラムダ式に触れる機会が増えてきたので、ここらへんで自分の知識をまとめておきたいと思う。
まずラムダ式とはなにか。関数型インターフェース(1つのインターフェースに実装が必要なメソッドを1つだけ持つインターフェース)に対して、以下のような形式で記述する文法である。
(引数) -> {処理};
では、実際にどのようになるか見てみよう。まずはjava.util.function.Functionインタフェースに対して、ラムダ式を使わずに匿名クラスで記述した場合。
public void testMethod() { Function<Integer, Double> function = new Function<Integer, Double>() { @Override public Double apply(Integer t) { return (double) t / 2; } }; System.out.println(function.apply(1)); }
では、上記をラムダ式で記述するとどのようになるか。
public void testMethod() { Function<Integer, Double> function = (Integer t) -> { return (double) t / 2; }; System.out.println(function.apply(1)); }
慣れないと分かりにくいが、匿名クラスの作成やメソッド部分が省略され、ずいぶん記述量が減った。
ここから若干、駆け足で説明したいと思おう。まずは引数の型は定義されているため、記述を省略できる。(ここではジェネリックス型なので左辺で定義されている。ジェネリックス型以外であればインタフェースに定義済みである。)
public void testMethod() { Function<Integer, Double> function = (t) -> { return (double) t / 2; }; System.out.println(function.apply(1)); }
さらに引数が一つであれば括弧()が省略できる。
public void testMethod() { Function<Integer, Double> function = t -> { return (double) t / 2; }; System.out.println(function.apply(1)); }
また戻り値が一つであれば return を省略できる。
public void testMethod() { Function<Integer, Double> function = t -> (double) t / 2; System.out.println(function.apply(1)); }
最初の匿名クラスの場合と比較してみてほしい。たった一行でインターフェースの実装が完了してしまった!!
これがラムダ式の力である。
もちろん慣れるまで意味を把握しにくいかもしれないが、この記述量の削減には素晴らしいものがある!
それにJava8から追加された「Stream API」ではラムダ式が多用されるため、
ぜひとも早く慣れて使いこなせるようになっておいたほうがいい。
最後にいくつか補足を付け加える。
まずは引数がない場合、どのようになるか。答えは ()-> {処理}; となる。
ここではjava.util.function.Supplierについて例を記載する。
public void testMethod_Supplier() { Supplier<String> supplier = () -> "supplier"; // supplierと表示。 System.out.println(supplier.get()); }
またメソッド参照ができるので、このような記載も可能。
ここではjava.util.function.Consumerについて例を記載する。
public void testMethod() { Consumer<String> consumer = System.out::println; // consumerと表示。 consumer.accept("consumer"); }
Windows10にアップグレードしてみた② ~アプリ削除~
引き続きWindows10にアップグレードした後に気づいた便利な点、不便な点について触れたいと思う。2回目は不要なアプリの削除についてである。
■不便①
Windows10にアップグレードするとすぐに入れた覚えのないアプリがインストールされていることに気づく。WindowsのPCを買ったときと同じで、いらないアプリを削除するというお決まりの作業が発生するのである。ここら辺はMicrosoftさんに改善してほしい点だ。
基本はメニュー画面で右クリックからアンインストールができるが、いくつか削除できないアプリがあることに気づく。例えば「3D Builer」や「People」、「モバイルコンパニオン」などだ。
そのようなアプリを削除する場合はひと手間必要である。Windows PowerShellを管理者権限で開き、Window8から搭載された「Remove-AppxPackage」コマンドで削除する必要があるのだ。
それでは具体的な使い方だ。ここでは例として「People」を削除してみよう。
まずは対象アプリの具体的な名称の特定だ。Windows PowerShellに「Get-AppxPackage *people*」と入力してみよう。アプリの詳細情報が表示される。
対象のアプリが分かったなら「Remove-AppxPackage 'PackageFullName'」コマンドだ。
これで不要アプリを削除できる。削除したアプリを復活させたい場合はWindowsストアから再度インストールすることで復活できる。
このようにひと手間加えなければアプリを削除できないのはMicrosoftさんに改善してほしいところである。
ついでにこれでも削除できないアプリ(「Edge」など)があるが、それについては今のところ削除する方法が分かっていないので、 知っている人がいれば教えてほしい。