How to get specific error instead of Internal Server Error? - java

I am getting Internal Server Error on postman even though I am throwing a custom exception from my code exception.
I want to see the exception of having a valid error message and error code, what I am throwing. It will be a great help if anyone of you can help me on this point. Like how I can get a better error message. Adding below code snap.
Thanks in advance.
#Service
public class FetchActionImpl implements FetchAction {
private static Logger log = LoggerFactory.getLogger(FetchActionImpl.class);
#Autowired
FetchActionServiceImpl fetchActionService;// = new FetchActionServiceImpl();
#Override
public FetchResponse fetchActionRequest(String caseId) throws BBWException,Exception{
//String resp ="";
log.info("fetchaction Request: {}",ApplicationConstants.LOG_ENTRY_MESSAGE);
log.info("The caseId received from BRASS:\n {}",caseId);
FetchResponse resp = null;
try{
if(true) {
throw new BBWException("500","Test");
}
resp = fetchActionService.fetchIt(caseId);
log.debug("fetchaction Response: {}",resp.toString());
}
catch (BBWException be) {
throw be;
}
catch (Exception e) {
throw new BBWException("500",e.getMessage());
}
return resp;
}
}
#Api
#Path("/fetch_service")
public interface FetchAction {
#GET
#Path("/fetchaction/caseid/{caseid}")
#Produces({MediaType.APPLICATION_JSON})
//#Consumes({MediaType.TEXT_XML})
#ApiOperation(
value = "Respond BRASS Request",
notes = "Returns a XML object "
)
#ApiResponses(
value = {
#ApiResponse(code = 404, message = "Service not available"),
#ApiResponse(code = 500, message = "Unexpected Runtime error")
})
public FetchResponse fetchActionRequest(#PathParam("caseid") String caseid) throws BBWException, Exception;
}`
public class BBWException extends Exception {
private static final long serialVersionUID = -7987978270260338068L;
private String errorCode;
private String errorMessage;
public BBWException(String errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
#Override
public String toString() {
return (this.errorCode + " " + this.errorMessage);
}
}

Each time the (uncaught) exception is thrown, SpringBoot returns Http-500 Internal Server Error. There are many ways of handling exceptions in Spring.
Let's say I have my controller and I implicitly throw an exception.
#RestController
public class HelloWorldController {
#GetMapping("/hello-world")
public String helloWorld() {
throw new MyCustomException("I just can't say hello!");
}
}
It's the same as yours - you can specify anything in the exception.
First:
One of the way of handling it, is to create a class with #ControllerAdvice annotation.
#ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handlyMyCustomException(MyCustomException e) {
logger.error("error occurred {}", e);
return new ResponseEntity<>("Something happened: " + e.getMessage(), HttpStatus.I_AM_A_TEAPOT);
}
}
This way you are able to catch the exception of your choice (globally) and return the message with the HTTP Response Status of your choice, not neccessarily I_AM_A_TEAPOT
Second:
#ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handlyMyCustomException(MyCustomException e) {
logger.error("error occurred {}", e);
return new ResponseEntity<>("Something happened: " + e.getMessage(), HttpStatus.I_AM_A_TEAPOT);
}
You could also create only method annotated with #ExceptionHandler in your controller class - but this is not global and is going to work only for this exact controller calls.
Result below:
Third:
Another way of dealing with exceptions is to create your own error .html files. If you place a file in resources/static/error/500.html it should be returned when the Http-500 Internal Server Error is thrown.

Related

How to catch `Request processing failed` for client param error in spring boot?

I register a DateTimeFormatterRegistrar in my class implements WebMvcConfigurer like this:
#Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
In the rest controller, i try to parse client GET params to an object:
#GetMapping("/api/url/path")
public APIResponse getPersonAttendList(#Valid SampleVO vo){}
SampleVO include field LocalDateTime time. If client offered wrong format of time param, the binding will be failed. Server will return 500, and print some log like this:
>ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] 175 - Servlet.service() for
servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;
nested exception is java.time.format.DateTimeParseException
My question is, how to catch this exception, and return 400 to the client? It seems ControllerAdvice is not working.
In my project I am using #RestControllerAdvice to handle such cases, an example would be,
#RestControllerAdvice
public class MyCustomExceptionsHandler {
#ExceptionHandler({HttpMessageNotReadableException.class})
public ResponseEntity handleException(HttpMessageNotReadableException httpMessageNotReadableException) {
//return the response entity you need here with the correct error
}
}
This is something that is working for me.
You should be able to catch this with a "try{}, catch(){}, and finally{}". You would try the code and then catch the exception, then return a 400 error to the client.
try {
//your code
} catch (java.time.format.DateTimeParseException e) {
//Return 400 here
} finally {
//Other code here
}
I'm sorry. I made a mistake here. First, i design to return client all response like
#Getter
#Setter
#AllArgsConstructor
public class APIResponse {
private Integer status;
private String msg;
private Object data;
#NotNull
public static APIResponse fromData(Object data) {
return new APIResponse(0, "", data);
}
#NotNull
public static APIResponse fromError(#NotNull BaseError err) {
return new APIResponse(err.getStatus(), err.getMsg(), null);
}
#NotNull
public static APIResponse fromError(#NotNull BaseError err, Object data) {
return new APIResponse(err.getStatus(), err.getMsg(), data);
}
#NotNull
public static APIResponse fromEmpty() {
return new APIResponse(0, "", null);
}
}
I made a global error catch like this:
#RestControllerAdvice
#Slf4j
public class ErrorWrapper {
private String getErrMsg(BindingResult bindingResult) {
StringBuilder stringBuilder = new StringBuilder();
for (FieldError error : bindingResult.getFieldErrors()) {
stringBuilder.append(error.getDefaultMessage()).append(";");
}
String msg = stringBuilder.toString();
log.debug("valid error:{}", msg);
return msg;
}
/**
* Bean validation error
* #see javax.validation.Valid
* #see org.springframework.validation.Validator
* #see org.springframework.validation.DataBinder
*/
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse paramNotValidHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
return APIResponse.fromError(new ParamError(getErrMsg(bindingResult)));
}
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse paramBindErrorHandler(BindException e) {
BindingResult bindingResult = e.getBindingResult();
return APIResponse.fromError(new ParamError(getErrMsg(bindingResult)));
}
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse paramConvertErrorHandler(MethodArgumentTypeMismatchException e) {
log.debug("valid error:", e);
return APIResponse.fromError(new ParamError("argument error:%s", e.getCause().getMessage()));
}
#ExceptionHandler(ServletRequestBindingException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse paramBindErrorHandler(ServletRequestBindingException e) {
return APIResponse.fromError(new ParamError("param bind error"));
}
#ExceptionHandler(InvalidPropertyException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse invalidProperty(InvalidPropertyException e) {
return APIResponse.fromError(new ParamError(e.getPropertyName() + " format error"));
}
#ExceptionHandler(PermissionError.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
private APIResponse permissionErrorHandler(PermissionError e) {
log.debug("not allowed:", e);
return APIResponse.fromError(e);
}
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
#ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
private APIResponse methodError() {
return APIResponse.fromError(new ClientError("HTTP Method error"));
}
#ExceptionHandler(MaxUploadSizeExceededException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse fileTooLarge() {
return APIResponse.fromError(new ClientError("file is too big"));
}
#ExceptionHandler(MissingServletRequestPartException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse badRequest(MissingServletRequestPartException e) {
return APIResponse.fromError(new ClientError("file not exist"));
}
#ExceptionHandler(HttpMessageConversionException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse badRequest(HttpMessageConversionException e) {
return APIResponse.fromError(new ClientError("can't parse"));
}
#ExceptionHandler(ClientAbortException.class)
private APIResponse clientAbortHandler(ClientAbortException e) {
return null;
}
#ExceptionHandler(ClientError.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse clientErrorHandler(ClientError e) {
log.debug("bad request:", e);
return APIResponse.fromError(e);
}
#ExceptionHandler(ServerError.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
private APIResponse serverErrorHandler(ServerError e) {
log.error("server error:" + e.getMsg(), e);
return APIResponse.fromError(e);
}
#ExceptionHandler(DataIntegrityViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse duplicateKeyError(DataIntegrityViolationException e) {
log.debug("duplicate source:", e);
return APIResponse.fromError(new ClientError("db unqiue key error"));
}
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
private APIResponse unknownErrorHandler(Exception e) {
String tips = "unknown error:";
if (e.getCause() != null) {
tips += e.getCause().getMessage();
} else if (e.getMessage() != null) {
tips += e.getMessage();
} else {
tips += e.toString();
}
log.error(tips, e);
return APIResponse.fromError(new ServerError());
}
So, if no catcher matched above, the last one will return 500.
There are so many exceptions for spring boot, i don't know how to catch them all and return 400 without missing any one.
If you want to catch all spring's exceptions in your controller advice then you need to catch Exception.class and then check the class name of the exception.
#Slf4j
#RestControllerAdvice(basePackages = "com.your.package.name")
public class ErrorWrapper {
#ExceptionHandler(Exception.class)
public APIResponse handleException(Exception ex, WebRequest request) {
if (ex.getClass().getCanonicalName().startsWith("org.springframework.")) {
return APIResponse.fromError(new ClientError(ex.getMessage()));
}
return APIResponse.fromError(new ServerError());
}
}
Surround this statement(DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar()) in try catch block .
try{
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
} catch (Exception e) {
e.printStacktrace();
}

Mockito doAnswer()

Can I somehow use doAnswer() when an exception is thrown?
I'm using this in my integration test to get method invocations and the test in configured the #RabbitListenerTest...
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyIT {
#Autowired
private RabbitTemplate rabbitTemplate;
#Autowired
private MyRabbitListener myRabbitListener;
#Autowired
private RabbitListenerTestHarness harness;
#Test
public void testListener() throws InterruptedException {
MyRabbitListener myRabbitListener = this.harness.getSpy("event");
assertNotNull(myRabbitListener);
final String message = "Test Message";
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(myRabbitListener).event(message);
rabbitTemplate.convertAndSend("exchange", "key", message);
assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
verify(myRabbitListener).messageReceiver(message);
}
#Configuration
#RabbitListenerTest
public static class Config {
#Bean
public MyRabbitListener myRabbitListener(){
return new MyRabbitListener();
}
}
}
It works ok but when I introduce an Exception being thrown, It doesn't i.e
This works
#RabbitListener(id = "event", queues = "queue-name")
public void event(String message) {
log.info("received message > " + message);
}
This doesn't
#RabbitListener(id = "event", queues = "queue-name")
public void event(String message) {
log.info("received message > " + message);
throw new ImmediateAcknowledgeAmqpException("Invalid message, " + message);
}
Any help appreciated
The LatchCountDownAndCallRealMethodAnswer is very basic
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
invocation.callRealMethod();
this.latch.countDown();
return null;
}
You can copy it to a new class and change it to something like
private volatile Exception exeption;
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
try {
invocation.callRealMethod();
}
catch (RuntimeException e) {
this.exception = e;
throw e;
}
finally {
this.latch.countDown();
}
return null;
}
public Exception getException() {
return this.exception;
}
then
assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
assertThat(answer.getException(), isInstanceOf(ImmediateAcknowledgeAmqpException.class));
Please open a github issue; the framework should support this out-of-the-box.

How to return error message from a method that otherwise returns a collection

It's more of a conceptual thing. My method is supposed to return a list of Conferences. But if there is an error, I just want it to send a String response or maybe a JSON response like {err: 'Some error'}.Offcourse following method throws compiler error for this line - return e.getMessage(); . How to achieve this?
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public List<Conferences> getAllConf(#RequestBody Conferences conf) {
List<Conferences> allConf = new ArrayList<Conferences>();
try {
allConf.addAll(confRepository.findAll());
} catch(Exception e){
return e.getMessage();
}
return allConf;
}
e.getMessage() returns a String and you method is a Conferences List, use a new generic response class like
public class Response {
private Object content;
private String error;
// getters and setters
}
and change your method
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public Response getAllConf(#RequestBody Conferences conf) {
Response resp = new Response();
List<Conferences> allConf = new ArrayList<Conferences>();
try{
allConf.addAll(confRepository.findAll());
resp.setContent(allConf);
}catch(Exception e){
resp.setError(e.getMessage());
}
return resp;
}
Have one option:
Best solution it is throw an exception:
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public List<Conferences> getAllConf(#RequestBody Conferences conf) {
List<Conferences> allConf = new ArrayList<Conferences>();
try {
allConf.addAll(confRepository.findAll());
} catch(Exception e){
throw new IllegalArgumentException(e.getMessage());
}
return allConf;
}
And create an error handler to handle exception and how you wanna display it:
#ControllerAdvice
public class CustomErrorHandler {
#ExceptionHandler(IllegalArgumentException.class)
public void handlerIllegalArgumentException(IllegalArgumentException exception, ServletWebRequest webRequest) throws IOException {
webRequest.getResponse().sendError(HttpStatus.BAD_REQUEST.value(), exception.getMessage());
}
}

Accepting multiple payloads from a Retrofit API request

My Retrofit API method is currently accepting one payload structure. However, the backend may return a different payload structure if there's any error in the request.
For example:
public void search(String term, final CallBack <ArrayList<String>> callBack) {
RetroGenerator.createService(APIServices.class).search(term).enqueue(new Callback<ArrayList<String>> () {
#Override
public void onResponse(Call<ArrayList<String>> call, Response<ArrayList<String>> response) {
if (response.isSuccessful()) {
callBack.onSuccess(response.body());
}
return;
}
callBack.onError();
}
#Override public void onFailure(Call<ArrayList<String>> call, Throwable t) {
callBack.onError();
}
});
}
The backend is returning an array of String values. However if an error occurs, backend may return the following payload structure:
{
"error": "Term can't be empty",
"code": 403
}
But the way my API method is setup, it only accepts one java model.
API Interface:
#FormUrlEncoded
#POST("api/v1/search.json")
Call<ArrayList<String>> search(#Field("term") String term);
Currently it's accepting only an ArrayList<String> and does not accept the custom error payload model. Given that I create a new model called Error:
public class Error {
public String error;
public int code;
}
How can I switch the retrofit API method's model when an error occurs?
You can have an ErrorUtils class to handle your unsuccessful responses:
public class ErrorUtils {
public static ApiError parseError(Response<?> response) {
Converter<ResponseBody, ApiError> converter = ServiceGenerator.retrofit().
responseBodyConverter(ApiError.class, new Annotation[0]);
ApiError apiError;
try {
apiError = converter.convert(response.errorBody());
} catch (IOException e) {
apiError = new ApiError();
}
return apiError;
}
}
Then when you find an unsuccessful response, just parse the response with the ErrorUtils class:
if (!response.isSuccessful()) {
// ...
ApiError apiError = ErrorUtils.parseError(response);
}
The ApiError class:
public class ApiError {
#SerializedName("error")
private String mErrorDescription;
#SerializedName("code")
private Integer mErrorCode;
public ApiError() {}
public void setErrorCode(Integer code) {
this.mErrorCode = code;
}
public Integer getErrorCode() {
return mErrorCode;
}
public String getErrorDescription() {
return mErrorDescription;
}
public void setErrorDescription(String errorDescription) {
mErrorDescription = errorDescription;
}
}

Java Rest Service Jersey User Defined Exception Handling

I have Created Java Rest service using Jersey Implementation.
My Requirement is to throw UserDefined Exception in my Rest Service
I have created Exception class and ExceptionMapper Classes
Here is my Java code:
public class AppServerException extends Exception {
private String errorMessage;
private int errorId;
private static final long serialVersionUID = 1L;
// Parameterless Constructor
public IT360AppServerException() {
}
// Constructor that accepts a message
public IT360AppServerException(String message, int errorid) {
super(message);
errorMessage = message;
errorId = errorid;
}
public String getErrorMessage() {
return errorMessage;
}
public int getErrorId() {
return errorId;
}
}
ExceptionMapper Class:
#Provider
public class AppServerExceptionMapper implements
ExceptionMapper<AppServerException> {
#Override
public Response toResponse(AppServerException exception) {
System.out.println("toResponse>>");
System.out.println("Error Code >>>>"+exception.getErrorId());////printing
System.out.println("Error Message >>>"+exception.getErrorMessage());//printing
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorCode(exception.getErrorMessage());
errorResponse.setErrorId(exception.getErrorId());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(errorResponse).type(MediaType.APPLICATION_JSON).build();
}
}
Problem:
I am able to print the logs inside toResponse() method but the response is not returning.Flow is stopping at the logs itself.Could any one help
RestService:
#POST
#Path("/update")
#Consumes("application/json")
#Produces("application/json")
public Response updateIT30(String json,
#HeaderParam("authorization") String authString)
throws UserAuthenticationException, IT360AppServerException {
System.out.println(" JSON Format" + json);
try{
response = client.execute(post);
}
catch(ConnectException e){
throw new IT360AppServerException("App Server is down", 403);
}
}

Categories