java-restify
Search…
Manipulação de erros
O java-restify oferece diferentes abordagens para manipulação e recuperação de erros.
Além dos erros de I/O (comunicação, rede, streams, etc) que podem ocorrer durante a requisiçao HTTP, o java-restify converte respostas HTTP 4xx (client error) e 5xx (server error) para exceções. Esse é o comportamento padrão, mas pode ser customizado para atender diferentes necessidades.
Todas as exceções do java-restify extendem com.github.ljtfreitas.restify.http.client.HttpException. Exceções ocorridas durante a execução do método ou durante a requisição HTTP sempre serão encapsuladas e propagadas em uma exceção do tipo HttpException.
Existem duas especializações dessa exceção:
  • HttpClientException, que representa erros de I/O
  • HttpMessageException, para exceções durante a manipulação da requisição e da resposta.
HttpMessageException possui uma subclasse chamada EndpointResponseException, que representa uma resposta HTTP de erro (status code 4xx ou 5xx). Essa classe possui várias sub-exceções, cada uma representando um status de erro HTTP específico. Isso permite implementar um controle fino para situações específicas:
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
Customer getCustomerById(@PathParameter String id);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
try {
12
Customer customer = myApi.getCustomerById("abc123");
13
14
} catch (EndpointResponseNotFoundException e) {
15
// 404 Not Found
16
17
} catch (EndpointResponseUnauthorizedException e) {
18
// 401 Unauthorized
19
20
} catch (EndpointResponseNotAcceptableException e) {
21
// 406 Not Acceptable
22
23
} catch (EndpointResponseException e) {
24
// qualquer outra resposta de erro HTTP
25
26
} catch (HttpClientException e) {
27
// erros de I/O
28
29
}
Copied!

Requisições assíncronas

Caso a requisição seja assíncrona, o tratamento de falhas deve ser implementado usando o próprio objeto de retorno ou com o uso de callbacks.
Por exemplo, caso o retorno seja um CompletableFuture:
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
CompletableFuture<Customer> getCustomerById(@PathParameter String id);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
myApi.getCustomerById("abc123")
12
.whenComplete((customer, exception) -> {
13
// lógica de falha ou sucesso
14
});
Copied!
Ou utilizando callbacks:
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallSuccessCallback<Customer> success, @CallbackParameter EndpointCallFailureCallback failure);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
myApi.getCustomerById("abc123",
12
customer -> { /* lógica de sucesso */},
13
exception -> { /*lógica de falha */});
Copied!
A interface EndpointCallFailureCallback (representada acima como uma expressão lambda) tem um único método, onFailure, que recebe um Throwable como parâmetro, permitindo o tratamento de qualquer exceção; existe uma especialização chamada EndpointResponseFailureCallback específica para respostas de erro, que permite uma manipulação fina de cada cenário de erro:
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
void getCustomerById(@PathParameter String id, @CallbackParameter EndpointCallSuccessCallback<Customer> success, @CallbackParameter EndpointResponseFailureCallback failure);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
EndpointResponseFailureCallback failure = new EndpointResponseFailureCallback() { // classe abstrata
12
13
// sobrescreva os métodos para cada resposta de erro que deseje manipular
14
15
@Override
16
protected void onNotFound(EndpointResponse<String> response) {
17
// 404 Not Found
18
}
19
20
@Override
21
protected void onUnauthorized(EndpointResponse<String> response) {
22
// 404 Not Found
23
}
24
25
@Override
26
protected void onNotAcceptable(EndpointResponse<String> response) {
27
// 404 Not Found
28
}
29
30
protected void onFailure(EndpointResponse<String> response) {
31
// qualquer outra resposta de erro HTTP
32
}
33
34
protected void onException(Throwable throwable) {
35
// qualquer outra exceção, incluindo erros de I/O
36
}
37
};
38
39
myApi.getCustomerById("abc123",
40
customer -> { /* lógica de sucesso */},
41
failure);
Copied!

Recuperação de respostas de erro

Além da captura da exceção, através de try/catch ou callback de falha, outra abordagem possível é implementar estratégias para recuperação de respostas de erro.
O objeto EndpointResponseErrorFallback é responsável pela manipulação de respostas 4xx e 5xx, podendo eventualmente retornar outra resposta. A implementação padrão é o comportamento demonstrado acima: a propagação de exceções específicas por tipo de resposta.
Mas implementações dessa interface podem implementar qualquer comportamento sobre respostas de erro. Por exemplo, um caso especial é o status code 404 (Not Found); respostas com esse status podem ser tratadas como uma resposta de corpo vazio, ao invés de uma exceção.
1
public interface MyApi {
2
3
// a API devolve 404, caso não encontre um Customer com o id
4
@Path("/customers/{id}") @Get
5
Customer getCustomerById(@PathParameter String id);
6
}
7
8
MyApi myApi = new RestifyProxyBuilder()
9
.error()
10
.emptyOnNotFound() // respostas 404 serão consideradas como uma resposta sem corpo
11
.target(MyApi.class)
12
.build();
13
14
// se a resposta for 404, o retorno do método será null
15
Customer customer = myApi.getCustomerById("xyz123");
Copied!
Uma solução elegante para a situação acima seria utilizar um Optional como retorno de método, representando uma resposta potencialmente vazia:
1
public interface MyApi {
2
3
// a API devolve 404, caso não encontre um Customer com o id
4
@Path("/customers/{id}") @Get
5
Optional<Customer> getCustomerById(@PathParameter String id);
6
}
7
8
MyApi myApi = new RestifyProxyBuilder()
9
.error()
10
.emptyOnNotFound() // respostas 404 serão consideradas como uma resposta sem corpo
11
.target(MyApi.class)
12
.build();
13
14
// se a resposta for 404, o retorno do método será um Optional vazio
15
Optional<Customer> customer = myApi.getCustomerById("xyz123");
Copied!
Também é possível implementar qualquer outra lógica para recuperação de respostas 4xx ou 5xx:
1
class MyResponseErrorFallback implements EndpointResponseErrorFallback {
2
3
@Override
4
public <T> EndpointResponse<T> onError(HttpResponseMessage response, JavaType responseType) {
5
/*
6
HttpResponseMessage é um objeto de nível mais baixo, que fornece acesso à resposta HTTP "crua".
7
O segundo parâmetro representa o tipo de retorno do método.
8
Implemente sua lógica de recuperação para retornar um EndpointResponse compatível com o tipo de retorno esperado, ou lançe uma exceção mais adequada ao seu domínio.
9
*/
10
}
11
}
12
13
MyApi myApi = new RestifyProxyBuilder()
14
.error(new MyResponseErrorFallback())
15
.target(MyApi.class)
16
.build();
17
18
// ou
19
20
MyApi myApi = new RestifyProxyBuilder()
21
.error()
22
.using(new MyResponseErrorFallback())
23
.target(MyApi.class)
24
.build();
Copied!

EndpointResponse

Conforme discutido na documentação sobre tipos de retorno, o objeto EndpointResponse representa a resposta HTTP completa, e pode ser utilizado como retorno de método.
Esse objeto possui alguns métodos que permitem implementar uma lógica de recuperação de falhas, em caso de respostas 4xx ou 5xx.
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
EndpointResponse<Customer> getCustomerById(@PathParameter String id);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
EndpointResponse<Customer> response = myApi.getCustomerById("abc123");
12
13
// o método recover recebe o tipo de exceção que deve ser manipulada, e uma funcão que retorna uma nova resposta
14
Customer customer = response.recover(EndpointResponseNotFoundException.class, e -> EndpointResponse.empty(StatusCode.ok()));
Copied!
O EndpointResponse deve ser manipulado com cuidado; ao tentar acessar o corpo, em caso de respostas de erro, será lançada a exceção correspondente ao status code:
1
public interface MyApi {
2
3
@Path("/customers/{id}") @Get
4
EndpointResponse<Customer> getCustomerById(@PathParameter String id);
5
}
6
7
MyApi myApi = new RestifyProxyBuilder()
8
.target(MyApi.class)
9
.build();
10
11
// digamos que a resposta desse request foi 500 (Internal Server Error)
12
EndpointResponse<Customer> response = myApi.getCustomerById("abc123");
13
14
Customer customer = response.body(); // será lançada uma exceção do tipo EndpointResponseInternalServerErrorException
Copied!
Last modified 2yr ago