基于CompletableFuture控制HTTP响应状态码的实现问题
Got it, let's tackle this problem step by step. You're looking to return proper HTTP status codes based on different scenarios in your async Spring MVC endpoint—404 when the task result is null, 5xx when exceptions occur, etc. Here's how to adjust your code to make that happen:
Step 1: Fix the Controller Method Access Modifier
First off, your getNewTasks() method is marked private—Spring MVC can't properly proxy and handle request mappings for private methods. You'll need to change it to public.
Step 2: Wrap Async Responses with ResponseEntity
To customize HTTP status codes, we'll use ResponseEntity to wrap our async result. Update the return type to CompletableFuture<ResponseEntity<Tasks>> so you can conditionally return 404 when the task result is null, or 200 when data exists.
Here's the revised Controller code:
@RestController public class TaskController { @Autowired private TaskService taskService; @GetMapping(path = "gettasks") public CompletableFuture<ResponseEntity<Tasks>> getNewTasks() { return taskService.getNewTasks() .thenApply(tasks -> { if (tasks == null) { return ResponseEntity.notFound().build(); // Returns 404 when no tasks exist } return ResponseEntity.ok(tasks); // Returns 200 with task data }); } }
Step 3: Handle Exceptions for 5xx Status Codes
Next, we need to catch exceptions and map them to appropriate 5xx status codes. A global exception handler is the cleanest approach, as it centralizes error handling for all your controllers.
Create a global exception handler class:
@RestControllerAdvice public class GlobalExceptionHandler { // Handle custom business exceptions (if you define any) @ExceptionHandler(BusinessTaskException.class) public ResponseEntity<String> handleBusinessException(BusinessTaskException ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); } // Handle general unexpected exceptions @ExceptionHandler(Exception.class) public ResponseEntity<String> handleGeneralException(Exception ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An unexpected error occurred while fetching tasks"); } }
Make sure your Service layer doesn't swallow exceptions—let them propagate so Spring can catch and handle them. Update your Service method to wrap exceptions properly with CompletableFuture.failedFuture():
@Service public class TaskService { @Async public CompletableFuture<Tasks> getNewTasks() { try { // Your business logic to fetch tasks goes here Tasks tasks = ...; return CompletableFuture.completedFuture(tasks); } catch (Exception ex) { // Propagate the exception instead of swallowing it return CompletableFuture.failedFuture(ex); } } }
Step 4: Optional: Fine-Tune 5xx Status Codes
If you need to distinguish between specific 5xx codes (like 500 vs 503), you can add targeted exception handlers for different exception types:
@ExceptionHandler(ResourceAccessException.class) public ResponseEntity<String> handleResourceUnavailable(ResourceAccessException ex) { return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("Task service is temporarily unavailable"); }
With these changes, your endpoint will now:
- Return
404 Not FoundwhentaskService.getNewTasks()returnsnull - Return appropriate
5xxstatus codes when exceptions occur during task retrieval - Maintain its asynchronous processing behavior
内容的提问来源于stack exchange,提问作者user871611

