How to fix issue RestControllerAdivse not working? - spring-boot

I have an issue related to RestControllerAdvice.
I have built an internal jar file as my own library and I implement some exception handler.
Anyway, that RestControllerAdvice is not working when have throw exception error.
RestControllerAdvice
#RestControllerAdvice
public class ApiControllerHandler {
#ExceptionHandler(ApiException.class)
public #ResponseBody
ApiResponse handleApiRequestException(ApiException e) {
ApiResponse response = new ApiResponse();
response.setCode(e.response.getCode());
response.setMessage(e.response.getMessage());
return response;
}
}
validator method
public static void request(JSONObject jsonReq, String requestKey) throws ApiException{
if (requestKey.isEmpty()) {
throw new ApiException("01", "Please input request validate key");
}
String key = jsonReq.getString(requestKey);
if (StringUtils.isEmpty(key)) {
throw new ApiException("01", requestKey + " Can not be null or empty.");
}
}
RestController
#PostMapping("/")
public String index(#RequestBody Map<String, Object> map){
JSONObject jsonObject = new JSONObject(map);
SPNValidator.request(jsonObject, "username");
return "Hello";
}
Request
{
"username" : ""
When post this request, exception will be throw because I already handled request not empty nor null
but my restControlleradvise is not working, it throws internal exception error.
Note: it works as normal if i use the same project,
but when build as jar file for other use, this function not work.
thanks.

One of the possible reason
When you build as internal JAR at that time spring dosen't know about any class in JAR so it will not search any package/Class from jar file so that's why your #RestControllerAdvice from internal JAR is not working
To solve this please use your internal JAR pacakge name in #ComponentScan
like below
#ComponentScan(basePackages = {"com.exception.base"})
so spring will inlcude ApiControllerHandler while scanning you project file.

Related

Olingo with Spring Boot

I am using this tutorial and it works for a simple java web application. Now I want to convert it to Spring Boot. I remove the web.xml and add the following two annotations to DemoServlet
#RestController
public class DemoServlet extends DispatcherServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(DemoServlet.class);
#RequestMapping("/DemoService.svc/*")
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
try {
// create odata handler and configure it with CsdlEdmProvider and Processor
OData odata = OData.newInstance();
ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>());
ODataHttpHandler handler = odata.createHandler(edm);
handler.register(new DemoEntityCollectionProcessor());
// let the handler do the work
handler.process(req, resp);
} catch (RuntimeException e) {
LOG.error("Server Error occurred in ExampleServlet", e);
throw new ServletException(e);
}
}
}
I also change the HTTPServlet to DispatcherServlet.
Now I am only able to access one end point. i.e.
http://localhost:8080/DemoService.svc/
The metadata end point is not working. It returns the service document instead of xml content.
http://localhost:8080/DemoService.svc/$metadata
Can somebody explain what is going on here?
user the below code for the process method.
handler.process(new HttpServletRequestWrapper(request) {
// Spring MVC matches the whole path as the servlet path
// Olingo wants just the prefix, ie upto /odata, so that it
// can parse the rest of it as an OData path. So we need to override
// getServletPath()
#Override
public String getServletPath() {
return "/DemoService.svc";
}
}, response);
You can create a #Configuration and Map your servlet in it like the following
#Bean
public ServletRegistrationBean odataServlet() {
ServletRegistrationBean odataServRegstration = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(),
"/DemoService.svc/*");
Map<String, String> initParameters = new HashMap<>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory",
"com.metalop.code.samples.olingo.springbootolingo2sampleproject.utils.JPAServiceFactory");
odataServRegstration.setInitParameters(initParameters);
return odataServRegstration;
}
Add the following after the handler.register call:
req.setAttribute("requestMapping", "/DemoService.svc");
The best implementation of olingo2 and spring-boot can be found here. I would suggest to take a look at this repository, it is very straight forward and easy.

Returning proper value from #AfterThrowing

I am new to String, SpringBoot.
Can we suppress thrown exception in a method annotated with #AfterThrowing?
I mean when an exception is thrown, it will suppress that and will return a default value on behalf of the invoking method?
Say, I have a controller -
#RestController
public class MyRestController implements IRestController{
#Override
#GetMapping("hello-throw")
public String mustThrowException(#RequestParam(value = "name")final String name) throws RuntimeException {
System.out.println("---> mustThrowException");
if("Bakasur".equals(name)) {
throw new RuntimeException("You are not welcome here!");
}
return name + " : Welcome to the club!!!";
}
}
I have created a #AspectJ, as follows -
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(* com.crsardar.handson.java.springboot.controller.IRestController.*(..))")
public void executionPointcut(){
}
#AfterThrowing(pointcut="executionPointcut()",
throwing="th")
public String afterThrowing(JoinPoint joinPoint, Throwable th){
System.out.println("\n\n\tMyAspect : afterThrowing \n\n");
return "Exception handeled on behalf of you!";
}
}
If I run this & hit a ULR like - http://localhost:8080/hello-throw?name=Bakasur
I will get RuntimeException, but, I want to return a default message like - Exception handeled on behalf of you!, can we do it using #AfterThrowing?
I know it can be done using #Around, but around will be called on every hit of the url, that I do not want
What you want to do is Exception Handling on the controller. You don't need to build it yourself, Spring already supports you with some annotations like #ExceptionHandler and #ControllerAdvice. Best would be to follow this example: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#using-controlleradvice-classes
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
#ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
You should use the fully qualified name of the class before method's name when you're referring to a pointcut. So, you should change #AfterThrowing something like this.
#AfterThrowing(pointcut="packageName.MyAspect.executionPointcut()",
throwing="th")
Please note that packageName is full package name of MyAspect.

Spring Boot Exception(Error) Handling for RESTful Services

I have the following RESTful Services method :
#PostMapping("/ajouterNewField")
public String ajouterField(#Valid #ModelAttribute("field") Fields field, Model model) throws IOException {
fieldDao.save(field);
// SOME CODE
return displayListeChamps( model);
}
The method is working fine and my question is how to handle any error (database not connected ...) or every issue that can happen durring the execution of this RESTful Services method.
You can use #ControllerAdvice
Refer to the code below
#ControllerAdvice
public String NyExceptionHandlerAdvice {
private final Logger logger = ...;
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({MyRunTimeException.class})
public void handleMyRunTimeException(Exception e) {
logger.error("Exception : ", e);
}
return MY_ERROR_STRING;
}
Best Practice is:
You can have your code throw RunTimeExceptions and handle all of them together or separately in handler methods similar to handleMyRunTimeException above.
You can decide what status code your request should return upon exception.
Basically you'll have to a sort of exception handler for any kind of exception your method might throw:
public class FooController{
// ...
#ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}
Here's a nice article about that: https://www.baeldung.com/exception-handling-for-rest-with-spring

Spring Boot Exception details

I am trying to log the exception in the spring boot based web service.
So I have used GlobalExceptionHandler
My code :
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e){
System.out.println("Ankit == "+e.getMessage());
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
System.out.println(errors.toString());
return e.getMessage();
}
}
the code is working fine. What I want is exception details. I mean the code where the exception occurs? File name / line ? or do I have to parse the stacktrace? I mean spring boot must have thought something for this?
Using IDE
If you are using any IDE then go to Console Window.
Clear console
Repeat action that causes Exception
Search in Console (CTRL + F) for ERROR
Look for line above(Look for 2-3 lines if you don't find immediate above) the line which contains ERROR. This line has details of Class, Method where Exception has occurred.
Without looking at Console or Logs
If you want to use it in production then, handling atleast known exceptions(like BAD_REQUEST, NOT_FOUND etc.) the way it is done below might be helpful (adding an extra parameter to Exception Class) :
Employee employee = employeeService.getEmployeeById(employeeId);
if (null == employee) {
logger.error("No tenant exists for employeeId:"+employeeId);
throw new ObjectNotFoundException("Emplyee Not Found", this.getClass().getSimpleName();));
}
here this.getClass().getSimpleName(); will be passed as parameter from EmployeeController class. So in ObjectNotFoundException we can add a parameter ClassName and When you handle it in GlobalExceptionHandler, you can do it as it is done below,
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e){
System.out.println("Ankit == "+e.getMessage());
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
String classWithExceptionName = e.getClassName();
// you need to add this above getter method to your Exception Class
System.out.println(errors.toString());
return e.getMessage();
}
}
This is for known common exceptions. We need to add extra parameter(ClassName) to All Custom Exceptions that you are throwing and that might be little extra code but i think that is the way. Hope it helps now.

Map UnsupportedMediaTypeException using ExceptionMapper

Is there a place where it is clearly documented that I cannot map UnsupportedMediaTypeException (because it's a rest easy exception and not custom application exception) using the javax.ws.rs.ext.ExceptionMapper?
I want to prove that to my client. Or another thing I would like to do is map this exception to a Response that can be fetched at the client to show the error. Right now when this exception is thrown it provides no information to the client as the application ends abruptly.
Any help would be appreciated.
Thanks
You can map this exception. Why not? Do you get an error?
This code should do the job
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
return Response.status(415).build();
}
}
Don't forget to declare that provider in Spring configuration file.
If you want to provide more information to the client create class
#XmlRootElement
public class Error{
private String message;
//getter and setter for message field
}
and then you can
#Provider
public class EJBExceptionMapper implements ExceptionMapper<org.jboss.resteasy.spi.UnsupportedMediaTypeException>{
Response toResponse(org.jboss.resteasy.spi.UnsupportedMediaTypeException exception) {
Error error = new Error();
error.setMessage("Whatever message you want to send to user");
return Response.entity(error).status(415).build();
}
}
If you don't want to use Error entity simply pass a string to Response.entity() call.
If you want to catch whatever is thrown in you application create generic exception mapper:
#Provider
public class ThrowableMapper implements ExceptionMapper<Throwable> {
public Response toResponse(Throwable t) {
ErrorDTO errorDTO = new ErrorDTO(code);
return Response.status(500).build();
}
}

Resources