API利用法
Spring BootでのAPI利用法
Section titled “Spring BootでのAPI利用法”Spring Bootアプリケーションで外部APIを利用する際の実践的な方法について詳しく解説します。
RESTful APIの設計原則
Section titled “RESTful APIの設計原則”リソース指向の設計
Section titled “リソース指向の設計”RESTful APIは、リソースを中心に設計します。
@RestController@RequestMapping("/api/users")public class UserController {
// GET /api/users - ユーザー一覧取得 @GetMapping public ResponseEntity<List<UserDTO>> getUsers( @RequestParam(required = false) String name, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { // 実装 }
// GET /api/users/{id} - ユーザー詳細取得 @GetMapping("/{id}") public ResponseEntity<UserDTO> getUser(@PathVariable Long id) { // 実装 }
// POST /api/users - ユーザー作成 @PostMapping public ResponseEntity<UserDTO> createUser(@Valid @RequestBody UserCreateRequest request) { // 実装 }
// PUT /api/users/{id} - ユーザー更新 @PutMapping("/{id}") public ResponseEntity<UserDTO> updateUser( @PathVariable Long id, @Valid @RequestBody UserUpdateRequest request) { // 実装 }
// DELETE /api/users/{id} - ユーザー削除 @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { // 実装 }}APIクライアントの実装
Section titled “APIクライアントの実装”RestTemplateを使用した実装
Section titled “RestTemplateを使用した実装”@Service@Slf4jpublic class ExternalApiService {
private final RestTemplate restTemplate; private final String apiBaseUrl;
public ExternalApiService(RestTemplate restTemplate, @Value("${external.api.base-url}") String apiBaseUrl) { this.restTemplate = restTemplate; this.apiBaseUrl = apiBaseUrl; }
public UserDTO fetchUser(Long id) { String url = apiBaseUrl + "/users/{id}";
try { ResponseEntity<UserDTO> response = restTemplate.getForEntity( url, UserDTO.class, id);
if (response.getStatusCode().is2xxSuccessful()) { return response.getBody(); } else { throw new ApiException("Failed to fetch user: " + response.getStatusCode()); } } catch (HttpClientErrorException.NotFound e) { throw new ResourceNotFoundException("User", id); } catch (Exception e) { log.error("Error fetching user: id={}", id, e); throw new ApiException("Error fetching user", e); } }
public UserDTO createUser(UserCreateRequest request) { String url = apiBaseUrl + "/users";
HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<UserCreateRequest> entity = new HttpEntity<>(request, headers);
try { ResponseEntity<UserDTO> response = restTemplate.postForEntity( url, entity, UserDTO.class);
if (response.getStatusCode().is2xxSuccessful()) { return response.getBody(); } else { throw new ApiException("Failed to create user: " + response.getStatusCode()); } } catch (Exception e) { log.error("Error creating user", e); throw new ApiException("Error creating user", e); } }}WebClientを使用した実装(推奨)
Section titled “WebClientを使用した実装(推奨)”@Service@Slf4jpublic class ExternalApiService {
private final WebClient webClient;
public ExternalApiService(WebClient.Builder webClientBuilder, @Value("${external.api.base-url}") String apiBaseUrl) { this.webClient = webClientBuilder .baseUrl(apiBaseUrl) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); }
public Mono<UserDTO> fetchUser(Long id) { return webClient.get() .uri("/users/{id}", id) .retrieve() .onStatus(HttpStatus::is4xxClientError, response -> { if (response.statusCode() == HttpStatus.NOT_FOUND) { return Mono.error(new ResourceNotFoundException("User", id)); } return Mono.error(new ApiException("Client error: " + response.statusCode())); }) .onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new ApiException("Server error: " + response.statusCode()))) .bodyToMono(UserDTO.class) .doOnError(error -> log.error("Error fetching user: id={}", id, error)) .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) .filter(throwable -> throwable instanceof WebClientException)); }
public Mono<UserDTO> createUser(UserCreateRequest request) { return webClient.post() .uri("/users") .bodyValue(request) .retrieve() .bodyToMono(UserDTO.class) .doOnError(error -> log.error("Error creating user", error)); }}APIキー認証
Section titled “APIキー認証”@Configurationpublic class RestTemplateConfig {
@Bean public RestTemplate restTemplate(@Value("${api.key}") String apiKey) { RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, execution) -> { request.getHeaders().set("X-API-Key", apiKey); return execution.execute(request, body); });
return restTemplate; }}Bearer Token認証
Section titled “Bearer Token認証”@Servicepublic class AuthenticatedApiService {
private final RestTemplate restTemplate; private final TokenService tokenService;
public AuthenticatedApiService(RestTemplate restTemplate, TokenService tokenService) { this.restTemplate = restTemplate; this.tokenService = tokenService; }
public UserDTO fetchUser(Long id) { String token = tokenService.getAccessToken();
HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(token);
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<UserDTO> response = restTemplate.exchange( apiBaseUrl + "/users/{id}", HttpMethod.GET, entity, UserDTO.class, id );
return response.getBody(); }}エラーハンドリング
Section titled “エラーハンドリング”@RestControllerAdvicepublic class ApiExceptionHandler {
@ExceptionHandler(HttpClientErrorException.class) public ResponseEntity<ErrorResponse> handleClientError(HttpClientErrorException ex) { ErrorResponse error = new ErrorResponse( ex.getStatusCode().value(), "Client Error", ex.getMessage() ); return ResponseEntity.status(ex.getStatusCode()).body(error); }
@ExceptionHandler(HttpServerErrorException.class) public ResponseEntity<ErrorResponse> handleServerError(HttpServerErrorException ex) { ErrorResponse error = new ErrorResponse( ex.getStatusCode().value(), "Server Error", "External API server error occurred" ); return ResponseEntity.status(ex.getStatusCode()).body(error); }
@ExceptionHandler(ResourceAccessException.class) public ResponseEntity<ErrorResponse> handleConnectionError(ResourceAccessException ex) { ErrorResponse error = new ErrorResponse( HttpStatus.SERVICE_UNAVAILABLE.value(), "Service Unavailable", "Failed to connect to external API" ); return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(error); }}リトライ機能
Section titled “リトライ機能”@Configurationpublic class RestTemplateConfig {
@Bean public RestTemplate restTemplateWithRetry() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
CloseableHttpClient httpClient = HttpClients.custom() .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) .build();
factory.setHttpClient(httpClient); return new RestTemplate(factory); }}キャッシング
Section titled “キャッシング”@Service@Cacheable("users")public class CachedApiService {
private final RestTemplate restTemplate;
@Cacheable(value = "users", key = "#id") public UserDTO fetchUser(Long id) { // API呼び出し return restTemplate.getForObject(apiBaseUrl + "/users/{id}", UserDTO.class, id); }
@CacheEvict(value = "users", key = "#id") public void evictUser(Long id) { // キャッシュをクリア }}レート制限の実装
Section titled “レート制限の実装”@Servicepublic class RateLimitedApiService {
private final RestTemplate restTemplate; private final RateLimiter rateLimiter;
public RateLimitedApiService(RestTemplate restTemplate) { this.restTemplate = restTemplate; this.rateLimiter = RateLimiter.create(10.0); // 秒間10リクエスト }
public UserDTO fetchUser(Long id) { rateLimiter.acquire(); // レート制限をチェック
return restTemplate.getForObject( apiBaseUrl + "/users/{id}", UserDTO.class, id); }}Spring BootでのAPI利用は、以下の要素を考慮して実装します:
- RESTful APIの設計: リソース指向の設計
- APIクライアント: RestTemplateまたはWebClientの使用
- 認証: APIキーやBearer Tokenの実装
- エラーハンドリング: 適切な例外処理
- リトライ機能: 一時的なエラーへの対応
- キャッシング: パフォーマンスの向上
- レート制限: APIの過剰な呼び出しを防止
これらの要素を適切に実装することで、堅牢で効率的なAPI連携を実現できます。