Como trabalhar com exceptions usando Rest

Pessoal, estou trabalhando com serviços usando RestFull (RestEasy) e estou tendo uma dificuldade.

Preciso lançar uma Exception criada por mim de validação de regras de negócio, por exemplo, e obter essa excpetion do lado cliente. Mesmo que nao seja a exception pelo menos implementar algo que eu receba a mensagem da exception lançada no servidor e crie e relance uma nova exception no lado cliente.

Ja busquei na documentação e as opções que encontrei sao muito soltas, muito “estranhas”.

Obrigado.

boa noite lindberg713.

trabalhando com rest, não há uma maneira de “repassar” uma exception do servidor para o client.

Pense que eles podem ser desenvolvidos em tecnologias distintas.

Em rest usamos o http como protocolo da aplicação. Quando algo da errado, por exemplo, o client envia uma requisição mal formada (xml faltando nós) vc retorna o status 400. Caso seja um erro no servidor, retorne o erro 500. Consulte http status code pra mais referencias…

Em ambos o casso vc pode retornar uma estrutura com mensagens de erros que podem ser lidas pelo seu client.

abrasssss

Obrigado renanreismartins. Entendi sim, acho que vou colocar a mensagem de erro no header do response. Ja testei aqui e vi que da pra pegar do outro lado a mensagem no lado cliente dessa forma. De qualquer forma. Muito obrigado pela resposta! Abraço.

Eu também estou olhando isso. O que o renanreismartins disse não é inteiramente verdade. A questão é que, pela exception não se propagar para o cliente (pois pode estar em outra linguagem), você acaba tendo que mapear essa exception no servidor e remonta-la no cliente.

Por exemplo, eu fiz uma classe que mapeia os dados de uma exception, algo bem simples. Segue:

public class BusinessExceptionMapper implements ExceptionMapper<BusinessException> {
	public Response toResponse(BusinessException exception) {
		ExceptionMapping em = new ExceptionMapping();
		em.setType(exception.getClass().getName());
		em.setMessage(exception.getMessage());
		
		String rootCause = null;
		String rootMessage = null;
		Throwable t = null;
		while ((t = exception.getCause()) != null) {
			rootCause = t.getClass().getName();
			if (t instanceof Exception) {
				Exception e = (Exception) t;
				rootMessage = e.getMessage();
			}
		}
		
		em.setRootCause(rootCause);
		em.setRootMessage(rootMessage);
		
		return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
				.entity(em).build();
	}
}

Com isso, estou criando (no meu caso em JSON) uma resposta baseada na classe:

@XmlRootElement
public class ExceptionMapping {
	private String type;
	private String message;
	private String rootCause;
	private String rootMessage;
	//gettes e setters
}

A resposta do servidor ficou assim (quando a exceção acontece):

Estou usando Spring com CXF, então, para mapear o ExceptionMapper, ficou assim:

<jaxrs:server id="restServer" address="/">
	<jaxrs:serviceBeans>
		<ref bean="bookService" />
	</jaxrs:serviceBeans>
	<jaxrs:providers>
		<bean class="org.foo.rest.app.BusinessExceptionMapper" />
	</jaxrs:providers>
</jaxrs:server>

Depois vem a parte do Client. Eu estou trabalhando nela agora. Ainda não consegui achar uma solução que funcione 100%, mas vou passar em que pé estou.

Criei o ExceptionMapper aqui também, mas nesse caso é o ResponseExceptionMapper, que faz o trabalho contrário do anterior, transformando a response em exception:

public class MeuResponseExceptionMapper implements ResponseExceptionMapper<BusinessException> {
	@Override
	public BusinessException fromResponse(Response r) {
		System.out.println("FROM RESPONSE!!!!");
		r.getEntity();
		return new BusinessException("MINHA MSG AQUI");
	}

}

Esse código está bem porquinho pois estou debugando pra ver se passa por aí (não consegui fazer passar ainda). Eu mapeei o MeuResponseExceptionMapper da seguinte maneira:

<jaxrs:client id="bookService"
	address="http://localhost:8082/poc-rest-cxf/services/bookService"
	serviceClass="org.foo.web.services.BookService"
	inheritHeaders="true">
	
	<jaxrs:providers>
		<bean class="org.foo.web.factory.MeuResponseExceptionMapper"></bean>
	</jaxrs:providers>
</jaxrs:client>

Mesmo principio do server, mas no jaxrs:client.

Lendo referências na internet, vi o seguinte:

Mas o meu não está sendo chamado… A partir daqui, se alguém poder ajudar a fazer funcionar, agradeço ^^

entao, mas no fim das contas:

vc intercepta sua exceção, monta sua ESTRUTURA

public class ExceptionMapping {  
    private String type;  
    private String message;  
    private String rootCause;  
    private String rootMessage;  
    //gettes e setters  
}  

e dps recebe ela no client, ai vc exibe as mensagens OU como vc está fazendo: relançando a exceção :slight_smile:

abrassss

Sim, mas se for transparante na camada view, arrisco dizer que repassa a exception…

Alguém sabe me ajudar nesse último passo no client? Não estou conseguindo fazer e acho que resolveria o problema do lindberg713 também.

Obrigado!

Consegui!

Todas as classes e configuração que passei estão corretos. O meu não estava funcionando por um motivo muito simples:

@Service
public interface BookService {
	@GET
	@Path("/books")
	@Produces({ MediaType.APPLICATION_JSON })
	public List<Book> getBooks();
}

Essa era a minha interface de serviço usada pelo rest.
Como estou usando CXF, ele procura por um ResponseExceptionMapper apenas das Exceptions que são lançadas pelo método de serviço.
Como o meu método não estava explicito que lança alguma, não passava pelo ResponseExceptionMapper que fiz. Para corrigir, coloquei o throws e tudo funcionou certinho:

@Service
public interface BookService {
	@GET
	@Path("/books")
	@Produces({ MediaType.APPLICATION_JSON })
	public List<Book> getBooks() throws BusinessException;
}