Spring Boot #ExceptionHandler working on dev server but not locally - java

I know this is usually an issue that happens the other way around, so I am caught a little of guard here :D
I have built a user-management backend that provides a UI with data. When this architecture is deployed on our dev-server, everything works beautifully. However, as soon as I try to run the integration tests (which we do using a maven cargo tomcat) or if I use the war file in a local tomcat, the exception handlers aren't used at all. Spring simply displays a standard 500 response with the exception transformed into the body.
Perusing SO for similar issues has only resulted in the advice that I should use #EnableWebMVC, but that is neither applicable to what my backend is trying to accomplish, nor does it change anything.
How should I go about looking for the solution to this issue? Specifically, can I somehow observe if my controlleradvice is even scanned, and is there a reason why it might not be?
EDIT: These are the relevant files:
SpringConfiguration:
#Configuration
#ComponentScan(basePackageClasses = {UserManagementSpringConfiguration.class})
#EnableWebSecurity
public class UserManagementSpringConfiguration {
#Configuration
public static class ResourceMappingConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ui/*/usermanagement").setViewName("forward:/usermanagement-ui/index.html");
// registry.addViewController("/ui/*/*/generator/").setViewName("forward:/generator-ui/index.html");
registry.addViewController("/ui/*/usermanagement/*").setViewName("forward:/usermanagement-ui/index.html");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// cache setting, otherwise fonts are not loaded in IE over https
CacheControl cacheControl = CacheControl.noCache().mustRevalidate();
registry.addResourceHandler("/ui/**/*").addResourceLocations("/usermanagement-ui/")
.setCacheControl(cacheControl);
}
}
}
ControllerAdvice:
#ControllerAdvice
public class CustomResponseEntityExceptionHandler {
public static final Logger LOG = EISLoggerFactory.getLogger(CustomResponseEntityExceptionHandler.class);
#PostConstruct
public void postConstruct() {
LOG.debug("CustomExceptionHandler loaded and ready for use");
}
#ExceptionHandler(PasswordMismatchException.class)
public final ResponseEntity<ErrorDetails> handlePasswordChangeMismatch(
PasswordMismatchException ex,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(
new Date(),
ex.getMessage(),
request.getDescription(false),
MessageKeys.mismatchedPassword);
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}

It turns out that one of the modules that we wrote and that my project contains defines an ExceptionHandler for Throwable.class. On my machine, this ControllerAdvice is registered before my own ControllerAdvice, which causes Spring to look there first. Since Throwable fits the bill, Spring asks no further questions and just uses that handler.
The solution to my immediate problem was to add #Order(Ordered.HIGHEST_PRECEDENCE) to my ControllerAdvice. Since the exceptions I define within are quite specific, this will not cause any issues.
I have yet to find an explanation for why the order in which the two ControllerAdvice classes are registered is so consistently different between my machine and our dev server. Will update if I find anything. For now, I consider this issue to be answered.
This SO question was essential to solving this particular problem. Perhaps it helps someone in the future to link it here: Setting Precedence of Multiple #ControllerAdvice #ExceptionHandlers
Thanks to ValentinCarnu for pointing me to it!

Related

Handle exceptions in a rest controller in Spring Boot

I create REST web-service with Spring Boot.
I would like to know what is a better way to handle exceptions in a controller. I have seen other questions and didn’t found an answer.
My controller:
#GetMapping
public ResponseEntity<?> saveMyUser(){
MyUser myUser = new MyUser(“Anna”);
//throws SQLException
MyUserDetails userDetails = userService.saveMyUser(myUser);
//if successful
return ResponseBody.ok(userDetails);
}
saveMyUser() method of UserService:
public MyUserDetails saveUser(MyUser) throws SQLException {...}
So at this point I have at least 2 simple options:
Add exception to method signature.
Here I may rely on Spring Boot to pass all information about exception and status code to a client. However do not know if it is a reliable approach.
Surround with try/catch and pass all information about exceptions manually.
What is a better simple way?
You can create an additional class with #ControllerAdivce annotation and later you will be able to write custom response logic for each exception e.g:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({SQLException.class})
public ResponseEntity<Object> sqlError(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Some SQL exception occured");
}
}
Also, you can extend ResponseEntityExceptionHandler and override the default behavior for mapping from exceptions to HTTP response.
Also, take a look at this, it holds very usefull information for your case.

How to properly extend WebAuthenticationDetails?

Building Spring Boot application, deploying(by copying to webapps folder while Tomcat is down) to local Tomcat8. Always get an error:
No thread-bound request found:
Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread?
If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet:
In this case, use RequestContextListener or RequestContextFilter to expose the current request.
As I understand, problems are while instantiating bean with WebAuthenticationDetails extending:
#Component
public class AuthDetails extends WebAuthenticationDetails{
private final AuthTarget authTarget;
public AuthDetails(HttpServletRequest request) {
super(request);
this.authTarget = AuthTarget.valueOf(request.getParameter("target"));
}
public AuthTarget getAuthTarget(){
return this.authTarget;
}
}
It cannot provide HttpServletRequest for bean constructing, but I don't know how to evade it.
Tried to add RequestContextListener, in xml or as implementation(and marking as #WebListener), no effect.
Out of ideas, how to fix it. Tried example from here: https://github.com/Baeldung/spring-security-registration , no changes - same error at the similar place.
Any help is greatly welcome.
The key was simple: I should define filter bean explicitly in security configuration extends WebSecurityConfigurerAdapter:
#Bean
AuthFilter authFilter() throws Exception{
AuthFilter authFilter = new AuthFilter();
return authFilter;
}
instead of autowiring it.

Why doesn't my spring boot error controller work?

I have a simple spring boot web application (running under Tomcat) and I cannot figure out how to get an error handler to work. So far I've created the web application and controllers and they all work fine. That is, they display Thymeleaf templates. Upon error. I get the default white label error page.
I first disabled this white label error page by adding an EnableAutoConfiguration annotation, thus:
#SpringBootApplication(scanBasePackages="au.com.sample")
#EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
public class SampleWebApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SampleWebApp.class);
}
}
Now, if I browse to an unknown page I get a generic 404 HTML error page containing:
<html>...<body><h1>HTTP Status 404 – Not Found</h1></body></html>
So that step worked. Now when I try and add my own error controller it does not trigger when I browse to an end point that does not exist:
#Controller
#RequestMapping("/")
public class ErrorHandlingController implements ErrorController {
#Override
public String getErrorPath() {
return "/error";
}
#RequestMapping(/"error")
public ModelAndView handleError() {
ModelAndView mav = new ModelAndView();
mav.addObject("title", "Error");
mav.setViewName("layout");
return mav;
}
}
"layout" is my existing thymeleaf template that works fine for my other end points.
I've tried several variations of the error controller, eg. changing the endpoint to "error" (not "/error"), using #RestController instead of #Controller but with no luck. In every case, I get the same generic 404 HTML instead of my hand-crafted template.
Can anyone see what I'm doing wrong or have any tips on how to track down my problem.
To show a custom error page for a specific status is pretty easy in Spring Boot. Just add an <error-code>.html into the src/main/resources/templates/errors directory to have it resolved. Or add a generic error.html as a fallback. See the reference guide for this feature (see also this).
If you want to add your own error handling just add a bean of type ErrorController or if you want to only add attributes add an ErrorAttributes typed bean to your configuration. See this section for more information.
A late answer to the original question of why Spring doesn't call your error controller: I think you are excluding the magic that makes the custom ErrorController work. In a Spring-Boot v2.1.6 app I had to allow (NOT exclude) the ErrorMvcAutoConfiguration class, then it used a custom MyController-implements-ErrorController class on page-not-found situations etc. It seems to be an either-or situation. I think you should only exclude the ErrorMvc.. class if you are using Thymeleaf templates AND you have published template files in a templates/ directory, because in that case you don't really need a custom error controller. HTH.

Wildfly Swarm RESTeasy hides webapp/index.html

I am working on a project based on Wildfly Swarm. The problem I currently have is that RESTeasy hides my index.html (and other html files) which are placed below /webapp since RESTeasy is listening on root level.
My Main Application:
#ApplicationPath("/")
public class XYZnlineApplication extends Application {
}
One of my resources:
#Path("protected/api/admin")
public class AdminResource {
#GET
#Path("public/api/offer/reduced")
#Produces("application/json")
public List<XYZ> getXYZ() {
...
}
#GET
#Path("protected/api/offer/full")
#Produces("application/json")
public List<XYZ> getAllXYZ() {
...
}
}
The thing is. If I start my wildfly swarm app and access one of the restendpoint above, everything works fine (e.g. http://localhost:8080/app/public/api/offer/reduced)
But if I d'like to access one of my html (e.g. login.html) files which are directly below /webapp, I get a 404 although the file is bundled correctly (e.g. on trying to access http://localhost:8080/app/login.html). So in my opinion what happens is that RESTeasy hides this html file cause it listens on root (/).
Since the first part of my url is the context (which is injected by a proxy) I can't set anything else than root (/) as ApplicationPath in my XYZApplication.
Do you have any idea on how I could solve this issue?
Thanks a lot in advance for your help.
You'll need to change the ApplicationPath to be something like "/services" or "/svc" or whatever works for you. Ultimately you need to partition the URL namespace between static resources and services. You don't need to worry about the context when specifying the ApplicationPath.
Major Edit:
Your comment really explains what's going on. I'm not sure what type of security you're using exactly but ultimately you likely need to have a filter of some sort in front of your services. I would have something like:
#Provider
#Priority(Priorities.AUTHENTICATION)
#PreMatching
public class AuthFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest httpServletRequest;
#Context
private HttpServletResponse httpServletResponse;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if( containerRequestContext.getUriInfo().getPath().contains("/public/") )
return; // user is in public area - doesn't matter if they are authenticated
// guess at how to check if user is authenticated
if( httpServletRequest.getSession().get("user_is_ok") )
return;
httpServletResponse.sendRedirect("/login");
// or maybe
httpServletResponse.sendError(SC_UNAUTHORIZED);
}
}
Again, this is a bit of a guess but this is a pretty common way of handling your challenge.

Configure Response object for Rest Services inside a Jersey-Grizzly server, in OSGi container (CORS error prevention with Jersey 1x)

The last couple of days, I have been struggling with an issue. I've created a rest service hosted by a Grizzly server inside an OSGi container. Everything is working perfectly at this point.
Now, I want to add a header in every response.Not so complex or illogical right? Yet, I can't find a way to do it.
I have tried to:
1) Get the response object inside the rest functions as this question suggests (pretty textbook when you are not under OSGi).
2) Add a handler using the code above (in this case the service method is never called)
server.getServerConfiguration().addHttpHandler(
new HttpHandler() {
#Override
public void service(Request arg0, Response arg1)
throws Exception {
arg1.setHeader("Access-Control-Allow-Origin", "*");
}
});
I am using jersey-server/client/core 1.18.1 and grizzly2-server 1.18.1, hence i prefer a solution that can be applied in this version, but I am willing to update jar versions if it cannot be done in 1.18.x.
You could give a try to Jersey filters.
In a nutshell, you should create class implementing ContainerResponseFilter:
public class MyFilter implements ContainerResponseFilter {
#Override
public void filter(
ContainerRequest request,
ContainerResponse response
) throws IOException {
request.getHttpHeaders().add(<header name>, <header value>);
}
}
Then, you should register this filter in your Jersey server configuration.
Please, note, that this filter would be invoked on every response. To bind it only to specific resources, you could use annotation-binding, that is described here.
All other information you could find here.

Categories

Resources