Код ниже теперь работает, это другой код, чем мой оригинальный вопрос, но это почти та же идея (с Backend REST api и Database REST api).
My Database REST api:
@RestController
@RequestMapping("/user")
public class UserControl {
@Autowired
UserRepo userRepo;
@Autowired
UserMapper userMapper;
@GetMapping("/{login}")
public Mono<UserDTO> getUser(@PathVariable String login) throws DatabaseException {
User user = userRepo.findByLogin(login);
if(user == null) {
throw new DatabaseException(HttpStatus.BAD_REQUEST, "error.user.not.found");
}
return Mono.just(userMapper.toDTO(user));
}
}
UserRepo - это просто @ RestReporitory.
UserMapper использует MapStruct для сопоставления моего объекта с объектом DTO.
С:
@Data
@EqualsAndHashCode(callSuper=false)
public class DatabaseException extends ResponseStatusException {
private static final long serialVersionUID = 1L;
public DatabaseException(String message) {
super(HttpStatus.BAD_REQUEST, message);
}
}
@ Data & EqualsAndHashCode come из библиотеки Lombok.
Расширение ResponseStatusException очень важно здесь, если вы этого не сделаете, ответ будет плохо обработан.
My Backend REST API, который получает данные из Database REST API:
@RestController
@RequestMapping("/user")
public class UserControl {
@Value("${database.api.url}")
public String databaseApiUrl;
private String prefixedURI = "/user";
@GetMapping("/{login}")
public Mono<UserDTO> getUser(@PathVariable String login) {
return WebClient
.create(databaseApiUrl)
.get()
.uri(prefixedURI + "/{login}", login).retrieve()
.onStatus(HttpStatus::isError, GlobalErrorHandler::manageError)
.bodyToMono(UserDTO.class);
}
}
С GlobalErrorHandler ::
public class GlobalErrorHandler {
/**
* Translation key for i18n
*/
public final static String I18N_KEY_ERROR_TECHNICAL_EXCEPTION = "error.technical.exception";
public static Mono<ResponseStatusException> manageError(ClientResponse clientResponse) {
if (clientResponse.statusCode().is4xxClientError()) {
// re-throw original status and message or they will be lost
return clientResponse.bodyToMono(ExceptionResponseDTO.class).flatMap(response -> {
return Mono.error(new ResponseStatusException(response.getStatus(), response.getMessage()));
});
} else { // Case when it's 5xx ClientError
// User doesn't have to know which technical exception has happened
return clientResponse.bodyToMono(ExceptionResponseDTO.class).flatMap(response -> {
return Mono.error(new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
I18N_KEY_ERROR_TECHNICAL_EXCEPTION));
});
}
}
}
и ExceptionResponseDTO, который является обязательным для извлечения некоторых данных из clientResponse:
/**
* Used to map <a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ClientResponse.html">ClientResponse</a> from WebFlux
*/
@Data
@EqualsAndHashCode(callSuper=false)
public class ExceptionResponseDTO extends Exception {
private static final long serialVersionUID = 1L;
private HttpStatus status;
public ExceptionResponseDTO(String message) {
super(message);
}
/**
* Status has to be converted into {@link HttpStatus}
*/
public void setStatus(String status) {
this.status = HttpStatus.valueOf(Integer.valueOf(status));
}
}
Один другой связанный класс, который может быть полезен : ExchangeFilterFunctions. java
Я нашел много информации в этом выпуске:
https://github.com/spring-projects/spring-framework/issues/20280
Даже если эта информация старая отдых плохо отпущен!