はじめに (対象読者・この記事でわかること)
この記事は、JavaとSpring Bootの基本的な知識があるWeb開発者を対象にしています。特に、REST APIの開発に携わっている方々に役立つ内容です。
この記事を読むことで、Spring Bootの@GetMappingアノテーションを使い、クエリパラメータの有無に関わらず柔軟にリクエストを処理する方法がわかります。具体的には、オプショナルなクエリパラメータの扱い方、複数のパターンに対応するエンドポイントの実装方法、そしてクエリパラメータのバリデーション方法を学ぶことができます。これにより、より堅牢でユーザーフレンドリーなAPIを開発するスキルが身につきます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - Javaの基本的な知識 - Spring Bootの基本的な理解 - REST APIの基本的な概念 - MavenまたはGradleの基本的な知識
クエリパラメータの基本と@GetMappingアノテーション
Webアプリケーション開発において、クエリパラメータはリクエストに追加情報を渡すための一般的な方法です。Spring Bootでは、@GetMappingアノテーションを使うことでHTTP GETリクエストを処理するエンドポイントを簡単に定義できます。
クエリパラメータはURLの「?」の後に「キー=値」の形式で記述され、「&」で区切って複数指定できます。例えば、「/users?id=123&name=John」というURLでは、「id」と「name」がクエリパラメータです。
Spring Bootでは、コントローラーメソッドの引数に@RequestParamアノテーションを使うことで、クエリパララメータをメソッド内で簡単に扱うことができます。例えば:
Java@GetMapping("/users") public String getUser(@RequestParam String id, @RequestParam String name) { return "User: " + name + " (ID: " + id + ")"; }
この例では、「/users?id=123&name=John」というリクエストが来た場合、「User: John (ID: 123)」というレスポンスが返されます。
クエリパラメータの有無を柔軟に処理する方法
実際の開発では、クエリパラメータが必須ではなく、オプショナルな場合が多くあります。また、同じエンドポイントで異なるパラメータの組み合わせに対応したい場合もあります。ここでは、そうしたケースに対応するためのいくつかの方法を解説します。
オプショナルなクエリパラメータの処理
クエリパラメータをオプショナルにするには、いくつかの方法があります。
方法1:引数にデフォルト値を設定する
@RequestParamアノテーションのrequired属性をfalseに設定し、引数にデフォルト値を指定する方法です。
Java@GetMapping("/users") public String getUser( @RequestParam(required = false, defaultValue = "guest") String name) { return "Hello, " + name; }
この例では、「name」パラメータがリクエストに含まれていない場合、「guest」というデフォルト値が使用されます。
方法2:Optional型を使用する
Java 8以降では、Optional型を使ってオプショナルな値を扱うことができます。
Java@GetMapping("/users") public String getUser(@RequestParam Optional<String> name) { return "Hello, " + name.orElse("guest"); }
この方法では、パラメータが存在しない場合、Optionalはemptyの状態になります。orElseメソッドを使ってデフォルト値を指定できます。
方法3:カスタムアノテーションを作成する
より複雑なデフォルト値のロジックが必要な場合は、カスタムアノテーションを作成することもできます。
Java@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface OptionalRequestParam { String defaultValue() default ""; String name() default ""; boolean required() default false; } @GetMapping("/users") public String getUser(@OptionalRequestParam(defaultValue = "guest") String name) { return "Hello, " + name; }
複数のパターンに対応するエンドポイントの実装
同じエンドポイントで異なるパラメータの組み合わせに対応する必要がある場合、いくつかの方法があります。
方法1:パラメータの有無で処理を分岐する
パラメータの有無に応じて処理を分岐する方法です。
Java@GetMapping("/search") public String search( @RequestParam(required = false) String keyword, @RequestParam(required = false) String category) { if (keyword != null && !keyword.isEmpty()) { return "Search results for: " + keyword; } else if (category != null && !category.isEmpty()) { return "Search results in category: " + category; } else { return "Please provide a search keyword or category"; } }
この方法では、リクエストに含まれるパラメータに応じて異なる処理を実行します。
方法2:異なるパスを定義する
パラメータの組み合わせによっては、異なるパスを定義する方が適切な場合もあります。
Java@GetMapping("/search/keyword") public String searchByKeyword(@RequestParam String keyword) { return "Search results for: " + keyword; } @GetMapping("/search/category") public String searchByCategory(@RequestParam String category) { return "Search results in category: " + category; }
この方法では、URLの構造が明確になり、APIの意図がより伝わりやすくなります。
クエリパラメータのバリデーション
クエリパラメータの値を検証することは、APIの堅牢性を高める上で重要です。Spring Bootでは、Bean Validation(JSR-380)を使ってクエリパラメータを簡単に検証できます。
方法1:@Validatedアノテーションとバリデーションアノテーションを使用する
コントローラークラスに@Validatedアノテーションを付け、パラメータにバリデーションアノテーションを追加します。
Java@RestController @Validated public class UserController { @GetMapping("/users") public String getUser( @NotBlank @Size(min = 3, max = 10) @RequestParam String name) { return "Hello, " + name; } }
この例では、「name」パラメータが空白でなく、3文字以上10文字以下であることを検証しています。検証に失敗すると、MethodArgumentNotValidExceptionがスローされます。
方法2:カスタムバリデーションロジックを実装する
より複雑なバリデーションロジックが必要な場合は、カスタムバリデーションを実装することもできます。
Java@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = CustomValidator.class) public @interface CustomValidation { String message() default "Invalid value"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } public class CustomValidator implements ConstraintValidator<CustomValidation, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { // カスタムバリデーションロジック return value != null && value.matches("[a-zA-Z0-9]+"); } } @GetMapping("/users") public String getUser(@CustomValidation @RequestParam String name) { return "Hello, " + name; }
実践的な例:ユーザーサーチAPIの実装
ここでは、実際にユーザーサーチAPIを実装しながら、クエリパラメータの有無を柔軟に処理する方法を具体的に見ていきましょう。
実装要件
- キーワードによるユーザー検索
- カテゴリーによるユーザー検索
- キーワードとカテゴリーの組み合わせによる検索
- ページネーション対応
実装コード
まず、検索条件を保持するDTOクラスを作成します。
Javapublic class UserSearchCriteria { @Size(max = 100) private String keyword; @Size(max = 50) private String category; @Min(0) private int page = 0; @Min(1) @Max(100) private int size = 10; // ゲッターとセッター }
次に、コントローラークラスを実装します。
Java@RestController @RequestMapping("/api/users") @Validated public class UserController { @GetMapping("/search") public ResponseEntity<List<User>> searchUsers( @Valid UserSearchCriteria criteria, BindingResult result) { if (result.hasErrors()) { // バリデーションエラーがある場合の処理 return ResponseEntity.badRequest().build(); } // 検索条件に基づいてユーザーを検索 List<User> users = userService.searchUsers(criteria); return ResponseEntity.ok(users); } }
サービスクラスでは、検索条件に基づいてユーザーを検索するロジックを実装します。
Java@Service public class UserService { public List<User> searchUsers(UserSearchCriteria criteria) { Specification<User> spec = Specification.where(null); if (StringUtils.hasText(criteria.getKeyword())) { spec = spec.and((root, query, cb) -> cb.or( cb.like(cb.lower(root.get("name")), "%" + criteria.getKeyword().toLowerCase() + "%"), cb.like(cb.lower(root.get("email")), "%" + criteria.getKeyword().toLowerCase() + "%") ) ); } if (StringUtils.hasText(criteria.getCategory())) { spec = spec.and((root, query, cb) -> cb.equal(root.get("category"), criteria.getCategory()) ); } // ページネーション Pageable pageable = PageRequest.of(criteria.getPage(), criteria.getSize()); Page<User> userPage = userRepository.findAll(spec, pageable); return userPage.getContent(); } }
この実装では、以下の点が特徴的です。
- UserSearchCriteriaというDTOクラスを使って検索条件を一元管理しています。
- @Validアノテーションを使って、検索条件のバリデーションを行っています。
- Specificationを使って動的に検索条件を組み立てています。
- ページネーションに対応しています。
使用例
このAPIは以下のように使用できます。
- キーワードで検索:
/api/users/search?keyword=taro - カテゴリーで検索:
/api/users/search?category=premium - キーワードとカテゴリーで検索:
/api/users/search?keyword=yamada&category=premium - ページネーション:
/api/users/search?keyword=t&page=0&size=5
まとめ
本記事では、Spring Bootの@GetMappingアノテーションを使い、クエリパラメータの有無に関わらず柔軟にリクエストを処理する方法について解説しました。
- オプショナルなクエリパラメータを扱う方法として、デフォルト値の設定、Optional型の使用、カスタムアノテーションの作成を紹介しました。
- 複数のパターンに対応するエンドポイントを実装する方法として、パラメータの有無で処理を分岐する方法と異なるパスを定義する方法を紹介しました。
- クエリパラメータのバリデーション方法として、Bean Validationとカスタムバリデーションの使用方法を紹介しました。
- 実践的な例として、ユーザーサーチAPIを実装し、クエリパラメータの柔軟な処理方法を具体的に示しました。
この記事を通して、より堅牢でユーザーフレンドリーなAPIを開発するスキルが身についたことと思います。今後は、より高度なクエリパラメータの処理方法や、パフォーマンスに関する最適化についても記事にする予定です。
参考資料
- Spring Framework Documentation - @RequestParam
- Spring Framework Documentation - @GetMapping
- Baeldung - Spring MVC Request Param
- Baeldung - Spring MVC Optional Request Parameters
- Baeldung - Spring MVC Validation
