#ExceptionHandler not being triggered? - spring

I have seen other duplicate stack overflow issues on this topic but none seem to replicate my situation.
When throwing an exception, my ExceptionHandler class is not picking it up and returning json, instead the default 500 code with the exception details is returned as HTML to the client. I have checked and Spring does initialise my ExceptionHandler class but for whatever reason the methods aren't called.
GlobalExceptionHandler.class:
#ControllerAdvice
#RequestMapping(produces = "application/json")
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
public GlobalExceptionHandler(){
LOG.debug("This gets called in logs...");
}
#ExceptionHandler({CustomException.class})
public #ResponseBody ResponseEntity<Object> handleCustomException(HttpServletRequest request,
CustomException ex) {
LOG.debug("This does not get called...");
Map<String, Object> response = new HashMap<>();
response.put("message", ex.getMessage());
return new ResponseEntity<>(response, ex.getCode());
}
}
CustomException.class:
public class CustomException extends RuntimeException{
private HttpStatus code;
private String message;
public CustomException(final HttpStatus code, final String message){
this.code = code;
this.message = message;
}
/**
* Gets message.
*
* #return Value of message.
*/
public String getMessage() {
return message;
}
/**
* Sets new code.
*
* #param code
* New value of code.
*/
public void setCode(HttpStatus code) {
this.code = code;
}
/**
* Sets new message.
*
* #param message
* New value of message.
*/
public void setMessage(String message) {
this.message = message;
}
/**
* Gets code.
*
* #return Value of code.
*/
public HttpStatus getCode() {
return code;
}
}
The exception handler is triggered here:
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain
filterChain) throws ServletException, IOException {
logger.debug("Filtering request for JWT header verification");
String jwt = getJwtFromRequest(request);
logger.debug("JWT Value: {}", jwt);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken
(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
logger.error("{}", new CustomException(HttpStatus.UNAUTHORIZED, "No Valid JWT Token Provided"));
throw new CustomException(HttpStatus.UNAUTHORIZED, "No Valid JWT Token Provided");
}
filterChain.doFilter(request, response);
}
}
I have all necessary properties in web config:
<!--<context:annotation-config />-->
<tx:annotation-driven/>
<context:component-scan base-package="com.app.controller"/>
My Web.xml:
<web-app>
<!-- For web context -->
<servlet>
<servlet-name>appDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Logging -->
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>/WEB-INF/classes/logback.xml</param-value>
</context-param>
<filter>
<filter-name>jwtFilter</filter-name>
<filter-class>com.app.controller.security.filters.JwtAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jwtFilter</filter-name>
<servlet-name>appDispatcher</servlet-name>
</filter-mapping>
</web-app>
Been going over this issue for a while..
This is all I get:

Your exception is not getting catched by the #ControllerAdvice because you are throwing it from a class annotated with #Component and not #Controller.
As per the documentation:
Specialization of #Component for classes that declare
#ExceptionHandler, #InitBinder, or #ModelAttribute methods to be
shared across multiple #Controller classes.
You can find a more complete reference here.

Related

How can I register a servlet with Spring Boot?

This code don't work
I have a web.xml that needs to be translated into a spring boot
<servlet>
<servlet-name>page-resolver</servlet-name>
<servlet-class>com.bk.ForwardServlet</servlet-class>
<init-param>
<param-name>page</param-name>
<param-value>/index.jsp</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>page-resolver</servlet-name>
<url-pattern>/index.html</url-pattern>
<url-pattern>/login</url-pattern>
<url-pattern>/qmgr/*</url-pattern>
</servlet-mapping>
and this class
#WebServlet
public class ForwardServlet extends HttpServlet {
private String page;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
page = config.getInitParameter("page");
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher(page).forward(req, resp);
}
}
I wrote code that registers this bean but it doesn't work and I don't understand why
#Configuration
public class ConfigureWeb implements ServletContextInitializer {
private void registerServlet(ServletContext servletContext) {
ServletRegistrationBean serviceServlet =
new ServletRegistrationBean(new ForwardServlet(), "/index.html","/login","/qmgr/*");
serviceServlet.addUrlMappings("/index.jsp");
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerServlet(servletContext);
}
}
First of all check and verify that all your resources are being kept in the correct directory.For spring-boot the jsp pages are to be kept in the directory src->main->webapp->WEB-INF->view->*.jsp .
You should have the following dependencies added in pom to compile the jsp properly when using embedded tomcat :
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- To compile JSP files -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
Now we need to tell spring where the jsp files reside, we can have two approaches for this :
Add entries in application.properties
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp
Here we can specify the folder where the jsp files are to be expected, as well as define the view.
or
Configure InternalResourceViewResolver to serve JSP pages
#Configuration
#EnableWebMvc
#ComponentScan
public class MvcConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
registry.viewResolver(resolver);
}
}
Now for registering servlets :
ServletRegistrationBean is used to register Servlets in Servlet 3.0 + container. We need to create a bean of ServletRegistrationBean in our JavaConfig. Find some methods of ServletRegistrationBean used to configure a Servlet.
setServlet(): Sets the servlet to be registered.
addUrlMappings(): Add URL mappings for the Servlet.
setLoadOnStartup: Sets priority to load Servlet on startup.
Suppose we have two Servlets as HelloCountryServlet and HelloStateServlet then we will register them with Spring Boot using ServletRegistrationBean as following :
#Configuration
public class WebConfig {
#Bean
public ServletRegistrationBean<HttpServlet> countryServlet() {
ServletRegistrationBean<HttpServlet> servRegBean = new ServletRegistrationBean<>();
servRegBean.setServlet(new HelloCountryServlet());
servRegBean.addUrlMappings("/country/*");
servRegBean.setLoadOnStartup(1);
return servRegBean;
}
#Bean
public ServletRegistrationBean<HttpServlet> stateServlet() {
ServletRegistrationBean<HttpServlet> servRegBean = new ServletRegistrationBean<>();
servRegBean.setServlet(new HelloStateServlet());
servRegBean.addUrlMappings("/state/*");
servRegBean.setLoadOnStartup(1);
return servRegBean;
}
}
For every Servlet we create a ServletRegistrationBean bean.
And the servlet class will look like :
public class HelloCountryServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
doGet(request,response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h3>Hello India!</h3>");
}
}
This is one approach.
Another method to register servlets in spring-boot is using annotations : #ServletComponentScan and #WebServlet. #ServletComponentScan in Spring Boot will scan Servlets annotated with #WebServlet, Filters annotated with #WebFilter and Listeners annotated with #WebListener.The annotation #ServletComponentScan is used on JavaConfig at class level. #ServletComponentScan scans Servlets, Filters and Listeners only using an embedded web server. Find the Servlets annotated with #WebServlet.
So the earlier HelloCountryServlet servlet can be provided as :
#WebServlet(urlPatterns = "/country/*", loadOnStartup = 1)
public class HelloCountryServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
doGet(request,response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h3>Hello India!</h3>");
}
}
and provide the ServletComponentScan annotation like :
#ServletComponentScan
#SpringBootApplication
public class SpringBootAppStarter {
public static void main(String[] args) {
SpringApplication.run(SpringBootAppStarter.class, args);
}
}
If you are not required to register a servlet, you could add the forwarding to the ViewControllerRegistry
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index.html").setViewName("forward:/index.jsp");
registry.addViewController("/login").setViewName("forward:/index.jsp");
registry.addViewController("/qmgr/**").setViewName("forward:/index.jsp");
}
}
You could combine the three addViewControllers into one using AntPathMatcher patterns
Map a view controller to the given URL path (or pattern) in order to
render a response with a pre-configured status code and view.
Patterns like "/admin/**" or "/articles/{articlename:\w+}" are
allowed. See AntPathMatcher for more details on the syntax.
Personally I think separating them is more readable

How to have jersey inject a param based on the http header?

I have an interface and would like to open it as a REST API.
Interface:
string createToken(String username, String scopes);
REST web API:
#GET
#Path("/createToken")
#Override
public string createToken(#InjectParam String username, String scopes) {
...
}
As a simple Java API,, the interface itself makes sense - creating an access token by a specific (unique) user.
But, as an REST web API, I need a previous step to retrieve the username, based on some user data that is passed in the http header, like an SSO key.
How do I inject a value into the username - extracted from the HTTP header? Thanks.
Created a Provider to inject the value into a custom annotation. See small working example here. See source inline below as well.
The example extracts the username from an sso token. It's a dummy extraction.
* I didn't use #InjectParam.
Invocation example:
curl -X POST -H "ssoToken: 1234" http://localhost:8080/JerseyCustomParamInjection-1.0-SNAPSHOT/oauth2/createAccessToken
Custom annotation:
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface LoggedUser {
}
Provider to do the injection:
#Provider
public class LoggedUserProvider implements
Injectable<String>,
InjectableProvider<LoggedUser, Parameter> {
#Context
private HttpServletRequest request;
public LoggedUserProvider() {
}
#Override
public Injectable<String> getInjectable(ComponentContext cc, LoggedUser a, com.sun.jersey.api.model.Parameter c) {
return this;
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public String getValue() {
String sso = request.getHeader("ssoToken");
if (sso == null) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
// Retreive username from soo
String username = " <extracted username from sso="+sso+">";
return username;
}
}
Resource that defines the wants to inject the value:
#Path("/oauth2")
public class Resource {
#POST
#Path("/createAccessToken")
public String createAccessToken(
#LoggedUser String username
) {
return username + " <created access token using the logged in injected username>";
}
}
Servlet configuration (web.xml):
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Restful Web Application</display-name>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>info.fastpace.jerseycustomparaminjection</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
You should use HeaderParam. See this
#GET
#Path("/createToken")
#Override
public string createToken(#HeaderParam("username") String username, String scopes) {
...
}
If you have to extract username and have to inject it, you will have to implement a provider:
#Provider
public class UsernameProvider
extends AbstractHttpContextInjectable<Locale>
implements InjectableProvider<Context, Type> {
#Override
public Injectable<E> getInjectable(ComponentContext compCntxt, Context cntxt, Type typ) {
if (typ.equals(String.class)) {
return this;
}
return null;
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public String getValue(HttpContext httpCntxt) {
final Request rqst = httpCntxt.getRequest();
String username = null;
//Extract 'username' from Headers
return username;
}
}
Detailed explanation here

Spring REST (MVC). How to modify http request before handling in the controller

I'd like to ask. How to modify http request before handling in the Spring REST (MVC) controller?
As far as I found I can make with servlets through filter and custom request wrapper. So does anyone know how to make it in Spring (e.g. via interceptors)?
My aim is to add new request parameter to request to use it into controller
Thx in advance
You can use Filter to modify httprequestparameter value. Watchout the implementation:
Web.xml
<filter>
<description>check request for service token</description>
<filter-name>CustomFilter</filter-name>
<filter-class>com.filters.CustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CustomFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Create a Filter Class:
public class CustomFilter implements Filter {
String encoding;
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("requestEncoding");
if (encoding == null) {
encoding = "UTF-8";
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// next handler
System.out.println("accessed");
filterChain.doFilter(new CustomFilterWrapper((HttpServletRequest)request),(HttpServletResponse)response);
}
public void destroy() {
}
}
Create a CustomFilterWrapper where you override getParameter method to modify certain request parameter. In my case I change value if the request param is maverick.
public class CustomFilterWrapper extends HttpServletRequestWrapper {
public CustomFilterWrapper(HttpServletRequest request) {
super(request);
}
#Override
public String getParameter(String name) {
String[] vals = getParameterMap().get(name);
if (name.equalsIgnoreCase("maverick")) {
return "modify value";
} else if (vals != null && vals.length > 0) {
return vals[0];
} else {
return null;
}
}
}

Jersey custom ExceptionMapper not called on 400 Bad Request (when validation fails)

I'm using Jersey with Spring for web services. For catching exceptions and formatting the response sent to the caller I have added an implementation of ExceptionMapper.
Though it is being called when I explicitly throw an exception from within the controller, but when the json field validation fails the exception mapper is not called and the response sent is **may not be null (path = checkNotification.arg0.tyres, invalidValue = null)
**
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
System.out.println("Exception Mapper !!!");
return Response.status(404).entity(ex.getMessage()).type("application/json").build();
}
}
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>
org.glassfish.jersey.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
com.help.rest.controller,
com.fasterxml.jackson.jaxrs.json
</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.help.filter.FeatureRegistration</param-value>
</init-param>
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Jersey Version is 2.22.1
Spring Version is 4.2.4
I made it work by changing
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
System.out.println("Exception Mapper !!!");
return Response.status(404).entity(ex.getMessage()).type("application/json").build();
}
}
to
#Provider
public class GenericExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException ex) {
System.out.println("Exception Mapper !!!");
return Response.status(404).entity(ex.getMessage()).type("application/json").build();
}
}
Though I'm able to catch the exception I am not getting the exact class and field which failed the constraint.
Resolved
Found a Set containing all the required fields in ContraintViolationException, could be accessed using ex.getConstraintViolations()
made it work by changing
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
System.out.println("Exception Mapper !!!");
return Response.status(404).entity(ex.getMessage()).type("application/json").build();
}
}
to
#Provider
public class GenericExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException ex) {
System.out.println("Exception Mapper !!!");
return Response.status(404).entity(ex.getMessage()).type("application/json").build();
}
}
To get the exact exception details, like which field failed the constraint use ConstraintViolationException's getConstraintViolations(). This method provides a set for all the constraint violations.
You need to register it:
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(HelloWorldResource.class);
/** you need to add ExceptionMapper class as well **/
s.add(GenericExceptionMapper.class)
return s;
}
}

How to setup default success status codes for mapped HttpMethods Spring MVC #RestControllers?

Hi there Spring Gurus,
Currently we are annotating every mapped #RestController method with it's own success #ResponseStatus. I wonder, if there is a possibility for a global setup, based on the mapped #RequestMapping method. On Success we'd like to have:
GET -> HttpStatus.OK
POST -> HttpStatus.CREATED
PUT -> HttpStatus.OK
PATCH -> HttpStatus.OK
DELETE -> HttpStatus.NO_CONTENT
If possible at all, what do we have to do?
Best regards,
Marius
You could do it using a Filter:
public class ResponseStatusConverterFilter implements javax.servlet.Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
chain.doFilter(request, response);
switch (request.getMethod()) {
case "GET": response.setStatus(200);
break;
case "POST": response.setStatus(201);
break;
...
}
}
And add filter in web.xml
<filter>
<filter-name>responseStatusConverterFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>responseStatusConverterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Just be careful with error handling because this could overwrite error response code. You should check response code before overwriting it (e.g check if it's some bad code like 500 or 404 and then do not set it to 200 or 201).
Another solution:
Create bean that extends HandlerInterceptorAdapter override
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
throws Exception
and add logic similar to my first solution.
Add bean definition to:
<mvc:interceptors>
<bean class="com.your.web.ResponseConverterInterceptor" />
</mvc:interceptors>
Addendum: You will need to wrap HttpServletResponse with your class that extends HttpServletResponseWrapper :
public class StatusConvertingServletResponse extends HttpServletResponseWrapper {
private int httpStatus;
public StatusConvertingServletResponse(HttpServletResponse response) {
super(response);
}
#Override
public void setStatus(int sc) {
httpStatus = sc;
super.setStatus(sc);
}
#Override
public int getStatus() {
return httpStatus;
}
}
And use it like:
StatusConvertingServletResponse res = new StatusConvertingServletResponse((HttpServletResponse)response);
chain.doFilter(request, res);
if (res.getStatus() == 200) {
res.setStatus(n);
}
Just use annotation on above your methods
#ResponseStatus(value=org.springframework.http.HttpStatus.OK)
#ResponseStatus(value=org.springframework.http.HttpStatus.CREATED)
#ResponseStatus(value=org.springframework.http.HttpStatus.OK)
#ResponseStatus(value=org.springframework.http.HttpStatus.OK)
#ResponseStatus(value=org.springframework.http.HttpStatus.NO_CONTENT)
You could extend the Spring #PostMapping and add the default #ResponseStatus i.e.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#PostMapping
#ResponseStatus(value = HttpStatus.CREATED)
public #interface CreatedPostMapping {
}
and then use that annotation instead of the spring ones e.g.
#CreatedPostMapping
public String create() {
return "blah";
}
Similarly for the other http verb annotations.

Resources