I am getting list of products from DB using REST Webservice Call & checking if products are NULL or NOT.
IF there are no products i need to throw an exception in POSTMAN.
Can anyone throw some light on how to show exception messages in postman?
Code:
public class ABC extends BusinessException
{
public ABC(final String message)
{
super(message);
}
public ABC(final String message, final Throwable cause)
{
super(message, cause);
}
}
you can directly use WebApplicationException from jax-rs to throw the exception
For Example:
if(products==null){
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).entity("products does not exist.").build());
}
If you have your custom exception then you can extends WebApplicationException
public class BusinessException extends WebApplicationException {
public BusinessException(Response.Status status, String message) {
super(Response.status(status)
.entity(message).type(MediaType.TEXT_PLAIN).build());
}
}
throw from your code
if(products==null){
throw new BusinessException(Response.Status.NOT_FOUND,"products does not exist.");
}
you can use error response object to display clean way
public class ErrorResponse {
private int status;
private String message;
ErrorResponse(int status,String message){
this.status = status;
this.message = message;
}
//setters and getters here
}
create ErrorResponse object while throwing the exception
public class BusinessException extends WebApplicationException {
public BusinessException(Response.Status status, String message) {
super(Response.status(status)
.entity(new ErrorResponse(status.getStatusCode(),message)).type(MediaType.APPLICATION_JSON).build());
}
}
In postman it will display like below
{
status:404,
message:"products does not exist."
}
Related
I have a service method that returns a UserDto object. However there are several situations where the request to the controller would return a HTTP status other than 200 but the service can only return null or the UserDto.
Is it bad practice to move some of the business logic to the controller and call the repository directly in order to return more detailed error messages, since the service cannot pass back an error message to the contoller?
You can do a bit better by having the service throw business exception, and the controller react on that. For example, a CustomerService could throw a `CustomerNotFoundException', and the controller could turn that into an appropriate HTTP status code, like this:
#ExceptionHandler({ CustomerNotFoundException.class })
public ResponseEntity handleException(CustomerNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
I recommend against moving the business logic to the controller, since the controller is more of an infrastructure component than domain logic. Also, consider adding another protocol, for example, a binary protocol, which wouldn't use the controller layer. You might miss your validations or business rules.
You can also use #ControllerAdvice from Spring to handle such cases, take a look at the below code.I hope this should help you return detailed error messages to the the controller.
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(NoSuchUserException.class)
public ResponseEntity<Object> handleNoSuchPinCodeException(
NoSuchUserException ex) {
ApiError apiError = new ApiError(HttpStatus.NOT_FOUND);
apiError.setErrorMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
}
public class NoSuchUserException extends Exception{
public NoSuchUserException (String message) {
super(message);
}
}
public class ApiError {
private HttpStatus status;
private String errorMessage;
private ApiError() {
}
public ApiError(HttpStatus status) {
this();
this.status = status;
}
public ApiError(HttpStatus status, String errorMessage, Throwable ex) {
this();
this.status = status;
this.errorMessage = errorMessage;
}
public HttpStatus getStatus() {
return status;
}
public void setStatus(HttpStatus status) {
this.status = status;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
I have a test case where am throwing exception incase of some basic validation. but ExceptionMapper is not being invoked. But if i run from postman to hit the service it is working fine.
Do Junit test have to run differently for ExceptionMapper ?
Test case :
#Test
public void itShouldHavePersonNumber() {
RestAuthController controller = new RestAuthController();
Response response = controller.insertGuid(null, "m012");
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> {controller.insertGuid(null, "m012");});
assertThat(response.getStatus()).isEqualTo(Status.BAD_REQUEST.getStatusCode());
}
Controller:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response insertGuid(#QueryParam("personNumber") Integer personNumber, #QueryParam("guId") String guId ) throws ValidationException {
if(guId == null || guId.isEmpty()) {
throw new ValidationException("guId is Required");
}
}
Exception Mapper :
#Provider
public class ValidationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException ex) {
return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
Exception:
public class ValidationException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public ValidationException() {
super();
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
}
Why do you think the exception mapper should be called? It is not an integration test. All you are doing is instantiating the class and then calling a method. There is nothing magical in Java that will make the exception mapper be called. You need to run an integration test with the Jersey application running (and the mapper registered) if you want the mapper to be called.
One way to run an integration test with Jersey is to use it's Test Framework. Below is an example.
public class ValidationExceptionTest extends JerseyTest {
public static class ValidationException extends RuntimeException {}
public static class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException e) {
return Response.status(400).entity("boo boo").build();
}
}
#Path("echo-name")
public static class EchoNameResource {
#GET
public String echoName(#QueryParam("name") String name) {
if (name == null || name.isEmpty()) {
throw new ValidationException();
}
return name;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(EchoNameResource.class)
.register(ValidationExceptionMapper.class);
}
#Test
public void testResponseOkWithQueryParam() {
final Response response = target("echo-name")
.queryParam("name", "peeskillet")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("peeskillet");
}
#Test
public void testResponseBadRequestWithNoQueryParam() {
final Response response = target("echo-name")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
}
}
I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?
I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}
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.
I have the following classes:
BusinessException.java (Is the main exception class that needs to catch the exception and throw it)
public class BusinessException
extends Exception
{
private BusinessExceptionInfo faultInfo
public BusinessException(String message, BusinessExceptionInfo faultInfo) {
super(message);
this.faultInfo = faultInfo;
}
public BusinessException(String message, BusinessExceptionInfo faultInfo, Throwable cause) {
super(message, cause);
this.faultInfo = faultInfo;
}
public BusinessExceptionInfo getFaultInfo() {
return faultInfo;
}
}
BusinessExceptionInfo.java
public class BusinessException {
protected ExceptionResponse exceptionResponse;
protected String message;
// getters and setters for these properties
}
ExceptionResponse.java (This class holds the exception messages and stack trace. This class needs to populated with the values coming from exception)
public class ExceptionResponse {
protected String exceptionClass;
protected String exceptionMessage;
protected List<ErrorResponseItem> errorResponseItems;
protected String stackTrace;
// getters and setters for these properties
}
ErrorResponseItem.java
public class ErrorResponseItem {
protected String defaultErrorDescription;
protected List<String> descriptionVariables;
protected String errorCode;
protected SourceSystemFault sourceSystemFault;
// getter and setter methods
}
// SourceSystemFault.java
public class SourceSystemFault {
protected List<String> calloutParameters;
protected String errorCode;
protected String errorDescription;
protected String operationName;
protected String serviceName;
protected String system;
}
MyClass.java (My class is where in I need to check and throw it. How can I pass the exception message, stack trace to ExceptionResponse from this class?)
Can any one share an example of similar issue. This will be helpful. I got to work with these file to handle exceptions in my project.
public MyResponse myMethod(MyRequest req)
throws BusinessException {
}
How can I catch and throw the BusinessException. I am a newbie and need help.