Spring controller tip malfunctioning CompletableFuture completed exclusively

I am using Spring Boot 1.5, and I have a controller that runs asynchronously, returning CompletableFuture<User>.

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private final UserService service;

    @GetMapping("/{id}/address")
    public CompletableFuture<Address> getAddress(@PathVariable String id) {
        return service.findById(id).thenApply(User::getAddress);
    }
}

The method UserService.findByIdmay call UserNotFoundException. So, I am developing a special advice for the dispatcher.

@ControllerAdvice(assignableTypes = UserController .class)
public class UserExceptionAdvice {
    @ExceptionHandler(UserNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public String handleUserNotFoundException(UserNotFoundException ex) {
        return ex.getMessage();
    }
}

The problem is that the tests do not pass, returning the status of HTTP 500, and not the status of 404 in case of an unknown user request to the controller.

What's happening?

+4
source share
1 answer

The problem is how the completed exception CompletableFuturehandles the exception in subsequent steps.

As stated in CompletableFuture javadoc

[..], () , , , , CompletionException . [..]

thenApply CompletionStage, CompletionException UserNotFoundException: (

, . Zalando : Async CompletableFuture append errors

, CompletableFuture Spring.

a CompletableFuture<T> a DeferredResult<T>. ​​ .

public class DeferredResults {

    private DeferredResults() {}

    public static <T> DeferredResult<T> from(final CompletableFuture<T> future) {
        final DeferredResult<T> deferred = new DeferredResult<>();
        future.thenAccept(deferred::setResult);
        future.exceptionally(ex -> {
            if (ex instanceof CompletionException) {
                deferred.setErrorResult(ex.getCause());
            } else {
                deferred.setErrorResult(ex);
            }
            return null;
        });
        return deferred;
    }
}

, .

@GetMapping("/{id}/address")
public DeferredResult<Address> getAddress(@PathVariable String id) {
    return DeferredResults.from(service.findById(id).thenApply(User::getAddress));
}

, Spring CompletableFuture , .

, .

+5

Source: https://habr.com/ru/post/1695827/


All Articles