JavaでPATCH APIを表現する
はじめに
JavaでREST APIをつくってるとPATCH
で困ったりしたことありませんか?
PATCH
リクエストはリソースの部分更新を行う際に使われるメソッドですね。
PUTリクエストとは異なり、完全にリソースの置き換えではなく、リクエストボディに含まれている属性のみを更新するものになります。
例えば以下のようなuserというリソースでニックネームだけをPATCHで更新したいとおもうと、
ID | 名前 | ニックネーム |
---|---|---|
1 | Bob | Bobby |
こういったリクエストが期待されます。
[例]ニックネームをBobbieにしたい
Path: /users/1
Method: PATCH
{
nickName: "Bobbie"
}
[例]ニックネームを消したい
Path: /users/1
Method: PATCH
{
nickName: null
}
今回はJavaで上記のようなリクエストを受け付ける際に、リクエストボディ(Json)でプロパティがそもそもないのか(更新対象ではない)、プロパティはあるけど値がnullなのか(nullで更新したい)をどう切り分けるんだ?と思ったので同じお悩みの方がいたらぜひ参考にしていただけたら幸いです。
対象読者
- Javaを使用したことがある方
- Spring BootなどメジャーなWebフレームワークの経験がある方
- REST APIを実装している方
技術スタック
- Java(openjdk 17.0.2)
- Spring Boot(3.4.0)
- Lombok
概要
JavaでREST APIを実装する際はSpring Bootを始めとしたフレームワークを使用することが多いと思います。
こういったモダンなフレームワークでは基本的に、jacksonなどを使用したJson<->POJOのマッピング機能が提供されており、冒頭で例としてあげたリクエストボディを以下のようなオブジェクトに自動でマッピングしてくれます。
@Data
public class PatchUserRequest {
private String name;
private String nickName;
}
Jsonマッピングを検証してみる
- nameを更新したい
{
"name":"Alice"
}
PatchUserRequest(name="Alice",nickName=null)
- nickNameをnullで更新したい(ニックネームを消したい)
{
"nickName":null
}
PatchUserRequest(name=null,nickName=null)
これでは1と2でnickNameをnullで更新したらいいのか、更新しちゃだめなのか区別がつきませんね。。
対策案(とっても簡単)
Optionalで包んであげるだけ
@Data
public class PatchUserRequest {
private Optional<String> name;
private Optional<String> nickName;
}
再検証
- nameを更新したい
{
"name":"Alice"
}
PatchUserRequest(name=Optional[Alice], nickName=null)
- nickNameをnullで更新したい(ニックネームを消したい)
{
"nickName":null
}
PatchUserRequest(name=null, nickName=Optional.empty)
これならnullのときはリクエストにない、Optional.emptyのときはリクエストにnullとして存在する。という判断ができます!
最後に
今回はSpringBootを使用して検証してみましたが、Jacksonを使用したPOJOマッピングであれば基本的にこの方法でいけそうです。Jakartaとか。
ちょっと探ってみた中では個人的にこれが一番しっくり、というか楽にできました。フレームワークなのでリクエストのインターセプター系でいい感じにできるかなと思いましたが、意外といいやり方がなかった。。。
こうすればいいじゃんっていうのがあったらぜひ教えてください!
Discussion