how can I custom error message in AOP around annotations? - java

I want to custom error message in AOP around annotations.
I used to use #RestControllerAdvice before but It didn't work in AOP around method.
it outputs default error message.
I tried to input message in try ~ catch I know it's weird like //// 1 or //// 2
But I can't get to the point :(
TransactionAspect class
#Around("execution(* com.bono.server.controller..*(..))")
#Transactional
public Object caculatePerformanceTime(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (CustomeException e) { ////// 1
throw new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
catch (Throwable throwable) { /////// 2
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
return result;
}
ErrorMessage class
#Getter
#Setter
public class ErrorMessage {
private int errorCode;
private String errorMessage;
public ErrorMessage(CustomError customError) {
this.errorCode = customError.errorCode();
this.errorMessage = customError.errorMessage();
}
}
GroupExceptionAdvice class
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class GroupExceptionAdvice extends ResponseEntityExceptionHandler {
//// 1
#ExceptionHandler(value = CustomeException.class)
public ResponseEntity<CustomErrorResponse> customhandleNotSatisfied(Exception ex, WebRequest request) {
CustomErrorResponse error = new CustomErrorResponse();
error.setTimestamp(LocalDateTime.now());
error.setError(ex.getMessage());
error.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
//// 2
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = CustomException.class)
public ErrorMessage handlerUnResolvedAddressException(MisTypingException e) {
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
}
{
"timestamp": "2019-08-12T01:14:16.467+0000",
"status": 500,
"error": "Internal Server Error",
"message": "class com.bono.server.config.exception.ErrorMessage cannot be cast to class org.springframework.http.ResponseEntity (com.bono.server.config.exception.ErrorMessage and org.springframework.http.ResponseEntity are in unnamed module of loader 'app')",
"path": "/bono/api/alarm"
}
I want to show like this
{
"code" : 102,
"message" : "got it !"
}

Converting my previous comments into an answer because the OP said that they solved his problem.
Your ErrorMessage class does not extend any Exception or Throwable, so how can you throw it? The code should not even compile and yield a compile error like:
No exception of type ErrorMessage can be thrown;
an exception type must be a subclass of Throwable
I.e. in your sample class I you ought to write something like
public class ErrorMessage extends Exception {
// (...)
}
for a checked exception or
public class ErrorMessage extends RuntimeException {
// (...)
}
for a non-checked exception. But your class definition does not extend anything, i.e. implicitly it directly extends Object.

Related

Exception handler for unexpected expceptions in Spock

Is there a way to handle unexpected exceptions in Spock? My use-case is to make test failures where exceptions are thrown better human-readable. E.g. when a test fails with an HttpClientErrorException, I would like to have the status code and the body in the printed test output.
e.g.
when:
restTemplate.getForObject(url, String)
then:
noExceptionThrown()
Now the getForObject() call throws an HttpClientErrorException I want an output like this:
Expected no exception to be thrown, but got 'HttpClientErrorException'
Status-Code: 400
Body: '{"error": "parameter foo missing"}'
You can write a custom extension to handle these exceptions yourself, you can't use noExceptionThrown() in this case, as this would prevent the exception to leave the feature method.
import spock.lang.*;
import java.lang.annotation.*;
import org.spockframework.runtime.extension.*;
import org.spockframework.runtime.model.*;
class NiceExceptionsInterceptor implements IMethodInterceptor {
static final NiceExceptionsInterceptor INSTANCE = new NiceExceptionsInterceptor()
void intercept(IMethodInvocation invocation) throws Throwable {
try {
invocation.proceed()
} catch (SpecialException e) {
throw new AssertionError("Special Exception happened: "+e.message)
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target([ElementType.TYPE, ElementType.METHOD])
#ExtensionAnnotation(NiceExceptionsExtensions)
#interface NiceExceptions {
}
class NiceExceptionsExtensions implements IAnnotationDrivenExtension<NiceExceptions> {
#Override
void visitSpecAnnotation(NiceExceptions annotation, SpecInfo spec) {
spec.bottomSpec.allFeatures.featureMethod*.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
}
#Override
void visitFeatureAnnotation(NiceExceptions annotation, FeatureInfo feature) {
feature.featureMethod.addInterceptor(NiceExceptionsInterceptor.INSTANCE)
}
}
// ---- Usage DEMO
class SpecialException extends Exception {
SpecialException(String message) {
super(message)
}
}
#NiceExceptions
class ASpec extends Specification {
def "spec level"() {
when:
throw new SpecialException("Foo")
then:
true
}
}
class BSpec extends Specification {
#NiceExceptions
def "test level"() {
when:
throw new SpecialException("Foo")
then:
true
}
}
Try it in the Groovy Web Console
You can write you handler logic and extract any information in the catch-block.
catch (SpecialException e) {
throw new AssertionError("Special Exception happened: "+e.message)
}

Return different HTTP status code from spring boot custom validators

I am using spring-boot version:2.0.5
Gradle:
buildscript {
ext {
springBootVersion = '2.0.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'io.reflectoring'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-validation')
implementation('org.springframework.boot:spring-boot-starter-web')
runtimeOnly('com.h2database:h2')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.junit.jupiter:junit-jupiter-engine:5.0.1')
// these dependencies are needed when running with Java 11, since they
// are no longer part of the JDK
implementation('javax.xml.bind:jaxb-api:2.3.1')
implementation('org.javassist:javassist:3.23.1-GA')
}
test{
useJUnitPlatform()
}
Controller
#RestController
class ValidateRequestBodyController {
#PostMapping("/validateBody")
ResponseEntity<String> validateBody(#Valid #RequestBody Input input) {
return ResponseEntity.ok("valid");
}
}
Validator class
class InputWithCustomValidator {
#IpAddress
private String ipAddress;
// ...
}
class IpAddressValidator implements ConstraintValidator<IpAddress, String> {
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Pattern pattern =
Pattern.compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
Matcher matcher = pattern.matcher(value);
//step 1
if (!matcher.matches()) {
return 400;
}
//Step 2
if (ipAddress already in DB) {
return 409; //conflict with other IP address
}
//Also I need to return different exception based on diff validations
}
}
ControllerAdvice
#ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handle(ValidationException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
If I throw customException from my validator then I used to get below error message even though I have corresponding controllerAdvice for it,
{
"code": "invalid_request"
"description": "HV000028: Unexpected exception during isValid call."
}
Always, I am getting 400 Bad request since I have a controllerAdvice which always return 400.
What I would like to achive here is, Is there any possibility to return customException with status code or is there anyway to return different status code from validator? I see similar posts in StackOverflow But there were no answer. I also checked other posts but I couldn't find it useful.
Current Behaviour
When an exception (which does not extend ConstraintDeclarationException) is thrown by the validator code instead of returning false, javax.validation wraps the exception in ValidationException. This is validator framework's behaviour and not a spring framework issue.
When an exception which extends ConstraintDeclarationException is thrown by the validator code instead of returning false, javax.validation framework propagates it.
If the validator returns false instead of throwing exception, Spring will convert all the validation errors into global errors or field errors and wrap them in MethodArgumentNotValidException and throws it.
Issue
The second option has field errors and global errors, custom status code can only be returned by inspecting the field name and error code. So this is not feasible as many fields can be added with that annotation.
In the first option, Custom exceptions which were thrown in the validator are wrapped in ValidationException so exception specific handler is not possible.
Possible Solutions
Unwrap the specific exception which does not extend ConstraintDeclarationException and map it
#ExceptionHandler(ValidationException.class)
public ResponseEntity handle(ValidationException e) {
Throwable cause = e.getCause();
if (cause instanceof InvalidIpException) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)//may be different code
.body(cause.getMessage());
}
if (cause instanceof InuseIpException) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)//may be different code
.body(cause.getMessage());
}
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
Make your specific exception to extend ConstraintDeclarationException and then have specific handler for it.
public class InvalidIpException extends
ConstraintDeclarationException {
#ExceptionHandler(InvalidIpException.class)
public ResponseEntity handle(InvalidIpException e) {
...
}
Reference code
https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintTree.java#L183
create a class for Error under the model package
#NoArgsConstructor
#Geeter
#Setter
public class ErrorMessage
{
private int errorCode;
private String errorMsg;
private String documentation;
public ErrorMessage(int errorCode,String errorMsg,String documentation)
{
this.errorCode=errorCode;
this.errorMsg=errorMsg;
this.documentation=documentation;
}
}
create a Custom Validation Exception class under the exception package
public class CustomValidationException extends RuntimeException
{
public CustomValidationException(String msg)
{
super(msg);
}
}
create ExceptionHandler class under the same(exception) package
#RestControllerAdvice
public class CustomValidationExceptionHandler
{
#ExceptionHandler
public ResponseEntity toResponse(CustomValidationException ex)
{
ErrorMessage errorMessage=new
ErrorMessage(400,ex.getMessage,"www.stackoverflow.com");
return new ResponseEntity<ErrorMessage>(errorMessage,HttpStatus.BAD_REQUES);
}
}
I will state my case here and you can apply my solution for your project I guess.
My annotation:
#Target({ ElementType.PARAMETER, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ResourceIdConstraintValidator.class)
public #interface ResourceId {
String message() default "Resource doesn't exist with given id.";
HttpStatus responseStatus() default HttpStatus.NOT_FOUND;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Keep in mind next line from annotation above, this is the key:
HttpStatus responseStatus() default HttpStatus.NOT_FOUND;
My validator:
#Service
#RequiredArgsConstructor
public class ResourceIdConstraintValidator implements ConstraintValidator<ResourceId, Integer> {
private final BinaryStorageService storageService;
#Override
public boolean isValid(Integer id, ConstraintValidatorContext context) {
return storageService.isResourceExist(id);
}
}
My Exception handler from my RestControllerAdvice:
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ExceptionResponse> handleConstraintViolationException(
ConstraintViolationException e,
HttpServletRequest request
) {
final List<String> messages = e.getConstraintViolations().stream()
.map(ConstraintViolation::getMessage)
.toList();
final HttpStatus httpStatus = e.getConstraintViolations().stream()
.findFirst().map(violation ->
(HttpStatus) violation.getConstraintDescriptor().getAttributes().get("responseStatus")
).orElse(HttpStatus.BAD_REQUEST);
final ExceptionResponse exceptionResponse = ExceptionResponse.builder()
.timestamp(ZonedDateTime.now())
.status(httpStatus.value())
.error(httpStatus.getReasonPhrase())
.message(messages)
.path(request.getServletPath())
.build();
return ResponseEntity.status(httpStatus)
.body(exceptionResponse);
}
So, I solved your issue in:
final HttpStatus httpStatus = e.getConstraintViolations().stream()
.findFirst().map(violation ->
(HttpStatus) violation.getConstraintDescriptor().getAttributes().get("responseStatus")
).orElse(HttpStatus.BAD_REQUEST);
After understanding the question fully, following is what will give you what you need. You can add as many exceptions as you need in the hierarchy as child classes of IpValidationException and keep custom HTTP status code as per your business/technical use cases.
public abstract class IpValidationException extends ValidationException {
private HttpStatus status;
private String message;
public IpValidationException(HttpStatus status, String message) {
this.status = status;
this.message = message;
}
public HttpStatus getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
public class InvalidIpException extends IpValidationException {
public InvalidIpException(HttpStatus status, String message) {
super(HttpStatus.BAD_REQUEST, "Invalid IP address");
}
}
public class InuseIpException extends IpValidationException {
public InuseIpException(HttpStatus status, String message) {
super(HttpStatus.CONFLICT, "IP address already in use");
}
}
public class IpAddressValidator implements ConstraintValidator<IpAddress, String> {
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Pattern pattern =
Pattern.compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
Matcher matcher = pattern.matcher(value);
//step 1
if (!matcher.matches()) {
throw new InvalidIpException();
}
//Step 2
if (ipAddress already in DB) {
throw new InuseIpException(); //conflict with other IP address
}
//Add more checks with more exception as you need.
}
}
// This is how your exception handler should look like
#ExceptionHandler(IpValidationException.class)
public ResponseEntity<ErrorResponse> handle(IpValidationException e) {
return ResponseEntity
.status(e.getStatus())
.body(e.getMessage());
}
Hope this helps!!

#RequestBody #Valid SomeDTO has field of enum type, custom error message

I have the following #RestController
#RequestMapping(...)
public ResponseEntity(#RequestBody #Valid SomeDTO, BindingResult errors) {
//do something with errors if validation error occur
}
public class SomeDTO {
public SomeEnum someEnum;
}
If the JSON request is { "someEnum": "valid value" }, everything works fine. However, if the request is { "someEnum": "invalid value" }, it only return error code 400.
How can I trap this error so I can provide a custom error message, such as "someEnum must be of value A/B/C".
The answer provided by #Amit is good and works. You can go ahead with that if you want to deserialize an enum in a specific way. But that solution is not scalable. Because every enum which needs validation must be annotated with #JsonCreator.
Other answers won't help you beautify the error message.
So here's my solution generic to all the enums in spring web environment.
#RestControllerAdvice
public class ControllerErrorHandler extends ResponseEntityExceptionHandler {
public static final String BAD_REQUEST = "BAD_REQUEST";
#Override
public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException exception,
HttpHeaders headers, HttpStatus status, WebRequest request) {
String genericMessage = "Unacceptable JSON " + exception.getMessage();
String errorDetails = genericMessage;
if (exception.getCause() instanceof InvalidFormatException) {
InvalidFormatException ifx = (InvalidFormatException) exception.getCause();
if (ifx.getTargetType()!=null && ifx.getTargetType().isEnum()) {
errorDetails = String.format("Invalid enum value: '%s' for the field: '%s'. The value must be one of: %s.",
ifx.getValue(), ifx.getPath().get(ifx.getPath().size()-1).getFieldName(), Arrays.toString(ifx.getTargetType().getEnumConstants()));
}
}
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setTitle(BAD_REQUEST);
errorResponse.setDetail(errorDetails);
return handleExceptionInternal(exception, errorResponse, headers, HttpStatus.BAD_REQUEST, request);
}
}
This will handle all the invalid enum values of all types and provides a better error message for the end user.
Sample output:
{
"title": "BAD_REQUEST",
"detail": "Invalid enum value: 'INTERNET_BANKING' for the field: 'paymentType'. The value must be one of: [DEBIT, CREDIT]."
}
#ControllerAdvice
public static class GenericExceptionHandlers extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException e, HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<>(new ErrorDTO().setError(e.getMessage()), HttpStatus.BAD_REQUEST);
}
}
I created a fully functional Spring boot Application with a Test on Bitbucket
You do not need #Valid for enum validation, you can achieve the required response using below code:
Controller Code, StackDTO has an enum PaymentType in it:
#RequestMapping(value = "/reviews", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> add(#RequestBody StackDTO review) {
return new ResponseEntity<String>(HttpStatus.ACCEPTED);
}
Create an exception class, as EnumValidationException
public class EnumValidationException extends Exception {
private String enumValue = null;
private String enumName = null;
public String getEnumValue() {
return enumValue;
}
public void setEnumValue(String enumValue) {
this.enumValue = enumValue;
}
public String getEnumName() {
return enumName;
}
public void setEnumName(String enumName) {
this.enumName = enumName;
}
public EnumValidationException(String enumValue, String enumName) {
super(enumValue);
this.enumValue = enumValue;
this.enumName = enumName;
}
public EnumValidationException(String enumValue, String enumName, Throwable cause) {
super(enumValue, cause);
this.enumValue = enumValue;
this.enumName = enumName;
}
}
I have enum as below, with a special annotation #JsonCreator on a method create
public enum PaymentType {
CREDIT("Credit"), DEBIT("Debit");
private final String type;
PaymentType(String type) {
this.type = type;
}
String getType() {
return type;
}
#Override
public String toString() {
return type;
}
#JsonCreator
public static PaymentType create (String value) throws EnumValidationException {
if(value == null) {
throw new EnumValidationException(value, "PaymentType");
}
for(PaymentType v : values()) {
if(value.equals(v.getType())) {
return v;
}
}
throw new EnumValidationException(value, "PaymentType");
}
}
Finally RestErrorHandler class,
#ControllerAdvice
public class RestErrorHandler {
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ValidationErrorDTO> processValidationIllegalError(HttpMessageNotReadableException ex,
HandlerMethod handlerMethod, WebRequest webRequest) {
EnumValidationException exception = (EnumValidationException) ex.getMostSpecificCause();
ValidationErrorDTO errorDTO = new ValidationErrorDTO();
errorDTO.setEnumName(exception.getEnumName());
errorDTO.setEnumValue(exception.getEnumValue());
errorDTO.setErrorMessage(exception.getEnumValue() + " is an invalid " + exception.getEnumName());
return new ResponseEntity<ValidationErrorDTO>(errorDTO, HttpStatus.BAD_REQUEST);
}
}
ValidationErrorDTO is the dto with setters/getters of enumValue, enumName and errorMessage. Now when you send POST call to controller endpoint /reviews with below request
{"paymentType":"Credit2"}
Then code returns response as 400 with below response body -
{
"enumValue": "Credit2",
"enumName": "PaymentType",
"errorMessage": "Credit2 is an invalid PaymentType"
}
Let me know if it resolves your issue.
Yon can achieve this using #ControllerAdvice as follows
#org.springframework.web.bind.annotation.ExceptionHandler(value = {InvalidFormatException.class})
public ResponseEntity handleIllegalArgumentException(InvalidFormatException exception) {
return ResponseEntity.badRequest().body(exception.getMessage());
}
Basically , the idea is to catch com.fasterxml.jackson.databind.exc.InvalidFormatException and handle it as per your requirement.
#Valid has to do with Hibernate bean validation. Currently enum type is not supported out of the box. I found this answer to be the closet, https://funofprograming.wordpress.com/2016/09/29/java-enum-validator/, the drawback however is that you have to make the enum field of type String instead.

SpringMVC: ExceptionHandler fails to catch my custom exception

In my main controller, when the exception is thrown, I want it to be catched by the ExceptionHandler in my error handling controller, but that never happens. Instead, I am getting Error 500. I am suspecting the problem is in #ResponseBody annotation of my main controller. Any idea how to achieve wanted behavior?
Main controller
#RequestMapping(value = "/person/{person}", method = RequestMethod.GET)
public #ResponseBody Person execute(#PathVariable(value = "person") String person) {
if(person.isValid(person)) {
return person;
} else {
throw new ResourceNotFoundException("Invalid person format.");
}
}
Exception
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable throwable) {
super(message, throwable);
}
public ResourceNotFoundException(Throwable throwable) {
super(throwable);
}
}
Error controller
private static final String ERROR_PAGE = "errors/error.jsp";
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView invalidApiCall(){
return generateView(ERROR_404);
}
private ModelAndView generateView(String errorCode) {
return new ModelAndView(ERROR_PAGE);
}
My error view never gets generated (#ExceptionHandler never catches the exception). Instead I am getting error 500. Is there a way for ExceptionHandler to catch my exception?
Try to add #ControllerAdvice annotation for the Error Controller. If it is already added, check whether the class' package is included in package scan.

JAX-RS jersey ExceptionMappers User-Defined Exception

I am new to this, trying to achieve reading some docs but its not working, please bear with me.
I have created a UserNotFoundMapper using ExceptionMappers like this:
public class UserNotFoundMapper implements ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
This in my service:
#GET
#Path("/user")
public Response getUser(#QueryParam("id") String id) throws UserNotFoundException{
//Some user validation code with DB hit, if not found then
throw new UserNotFoundException();
}
The UserNotFoundException is an User-Defined Exception.
I tried this:
public class UserNotFoundException extends Exception {
//SOME block of code
}
But when I invoke the service, the UserDefinedExceptionMapper is not getting invoked. It seems I might be missing something in the UserDefinedException. How to define this exception then?
Please let me know how to define the UserNotFoundException.
You need to annotate your exception mapper with #Provider, otherwise it will never get registered with the JAX-RS runtime.
#Provider
public class UserNotFoundMapper implements
ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain")
.build();
}
}
What I usually do when creating APIs is create my own exception that extends from RuntimeException so I don't necessarily have to catch my exception.
Here's an example:
NOTE: I'm using JAX-RS with Jersey
First: create my own Exception that extends from RuntimeException.
public class ExceptionName extends RuntimeException {
private int code;
private String message;
public int getCode(){
return code;
}
public String getMessage(){
return message;
}
public ExceptionName(int code, String message) {
this.code = code;
this.message = message;
}
}
Also implement a ExceptionMapper
#Provider
public class ExceptionName implements ExceptionMapper<ExceptionName>{
#Override
public Response toResponse(ExceptionName exception) {
return Response.status(exception.getCode()).entity(exception.getMessage()).build();
}
}
And every time that I want to throw an exception I just do it like this anywhere, the exception mapper will take care of returning a response to the client consuming the API
throw new ExceptionName(500,"there was an error with something here");
One small remark , try to Use Response.Status.NOT_FOUND rather than using 404 etc. Code will be more readable and less prone to typos , the same goes for "text/plain". Below is the code that will handle exception as you mentioned.
Oh and one more thing remember to annotate your method #Produces(MediaType.TEXT_PLAIN) in your interface
public class UserNotFoundException extends Exception {
//...
}
public class UserServiceImpl implements UserService {
#Override
public Response getUser(#QueryParam("id") String id) {
final Response response;
try{
// call user method
//if everything is ok
response = Response.status(Response.Status.OK).entity(whateverYouWant).type(MediaType.TEXT_PLAIN).build();
} catch(UserNotFoundException ex) {
response = new UserNotFoundMapper().toResponse(ex);
}
return response;
}
}
In client slide you can check
public static boolean isUserExists(final Response serverResp) {
return serverResp != null && serverResp.getStatus() == Response.Status.NOT_FOUND.getStatusCode();
}

Categories