I'm developing a web application with Java and Spring Boot. What I would like to do is to add an object into the Model every time a request is received. Let me explain better what I'm doing and why I need it.
The application is an eCommerce and I need every time a page is loaded the number of items inside the cart and the number of notifications a user has got. These information are displayed inside the menu in all the pages of the web app. Right now I'm requesting these information to through an ajax call after the page has been loaded. I would like to automatically add these information inside the Model and render and return all the pages with them already present without making any further request.
I googled it and I found out that a way to solve this problem is to use an Interceptor.
I implemented it following this tutorial but the only problem is that preHandle, postHandle and afterCompletion get called not only with the page requests but also with other kind of content like images, videos etc.
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre Handle method is Calling: " + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
When registering your interceptors in WebMvcConfigurerAdapter, you can also define a path pattern to include or exclude.
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new YourInterceptor()).excludePathPatterns("/path/to/your/static/resources/*");
}
Full example is available here : https://www.concretepage.com/spring/spring-mvc/spring-mvc-handlerinterceptor
Related
I'm using Spring boot version 2.0.5.RELEASE in my application. I have got more than 500 Restful APIs in the application. To these APIs, a new request header needs to be added. Is there a way to add header in one place and can be used by all the 500 APIs?
Yes you can write an interceptor for every request at root level and append your headers to that request.You can use prehandle as
This is used to perform operations before sending the request to the
controller.
Below is the code snippet
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {}
}
#RestController
#RequestMapping("/headerRequestPath)
Add this at the begining of your code file. That way all path's will be appended with header 'headerRequestPath'
Currently , I am implementing the hreflang meta tag in my site and I am facing a problem , I need to create a method that must be executed in every page (in order to load the hreflangs into the model and pass them to the JSP) of my site and this method must have this parameters HttpServletRequest request, HttpServletResponse response.
I tried with this :
#RequestMapping(value = {"/*"})
public class GLobalPageController {
#ModelAttribute
public void setupHreflangs(Model model, HttpServletRequest request, final HttpServletResponse response) {
//code
}
}
But its not working , any idea about how can I achieve this ?
We have some REST endpoints (e.g., /getalleuropeancars or /getalljapanesecars or /getallamericancars). These endpoints are all GET only.
Right now, each annotated endpoint class has its own code for checking for unauthorized methods (which is everything except GET). We would like one class to handle all unauthorized methods; you know, code re-use and all that.
Will a filter be able to distinguish between "GET /getalleuropeancars" and "POST /getalleuropeancars" and "DELETE /getalleuropeancars" and redirect to the proper place? Looking at servlet filters, it seems that they can only detect different URL paths, not different HTTP methods.
<url-pattern>/getalleuropeancars</url-pattern>
<url-pattern>/getalljapanesecars</url-pattern>
<url-pattern>/getallamericancars</url-pattern>
So, is a servlet-filter what we need?
Yes a Servlet Filter can do it. There are two things here, one is mapping the Filter to all the paths (url-patterns) you want it to filter. Second is to have it filter out the non-GET methods. Below is the code of a Filter that filters out non-GET requests. Jus fill in the //return error with your own code for returning an error.
public class OnlyGetsFilter implements Filter {
#Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
HttpServletRequest hsr = (HttpServletRequest) sr;
if (!"GET".equals(hsr.getMethod())) {
//return error
} else {
fc.doFilter(sr, sr1);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig fc) throws ServletException {
}
}
I'm working on a Jetty/RESTEasy app. If I throw a WebApplicationException(myResponse) from one of my REST endpoints, it sends the given response to the client.
When a filter detects an error, I want the same behavior:
It should stop execution from proceeding, and
It should give the user a clear, JSON-formatted error that does not include a stack trace.
Obviously, just writing to the response stream and returning works from within the doFilter method. But this doesn't work for other methods called by doFilter.
Throwing any exception will meet condition #1 but I haven't figured out a sane way to meet condition #2 then. (You can see my best attempt at the bottom.)
As Perception explained in his answer, WebApplicationExceptions are treated like any other exception in the context of a Filter, and therefore give the user a nice ugly stack trace.
So, to sum up my questions:
Do serveltt containers have any equivalent to throw new WebApplicationException(Response)?
And perhaps more importantly, how do other java projects handle this?
I have this code in one filter and it works, but I'd prefer a more elegant solution that automatically applies to all filters:
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
try {
doFilterOrThrow(request, response, chain);
} catch (WebApplicationException e) {
Response res = e.getResponse();
((HttpServletResponse) response).sendError(res.getStatus(), (String) res.getEntity());
}
}
The specific handling you mention for web application exceptions is only defined within the context of a JAX-RS container, which, by the way, is not the same thing as a Servlet container.
Web filters are handled by the Servlet container, which does not know or care that a JAX-RS container exists within the same application server. It also does not know or care about web application exceptions. So when you throw the WAE from within the filter it is treated just the same as any other exception (server error with a stack trace, or a preconfigured error page if you set one up in your web application).
It would seem to me if you are indicating an error to the client you could simply do so from the filter, by writing directly to the response stream. But if you are trying to leverage some existing JAX-RS logic then a (RESTEasy specific) solution would be to flag the request as error'ed out in your filter, then generate a WAE in JAX-RS, using a provider class. Example:
#WebFilter(urlPatterns = "*")
public class ForwardingFilter implements Filter {
#Override
public void destroy() {
return;
}
#Override
public void doFilter(final ServletRequest request,
final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
// Add an error response to be processed by the JAX-RS container.
// This would obviously be based on some condition.
request.setAttribute("errorResponse",
Response.status(500).entity("Didn't work out!").build());
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
return;
}
}
#Provider
#ServerInterceptor
#HeaderDecoratorPrecedence
#RequestScoped
public class ForwardingHandlerProvider implements PreProcessInterceptor {
#Override
public ServerResponse preProcess(final HttpRequest request,
final ResourceMethod method) throws Failure,
WebApplicationException {
final Response errorResponse = (Response) request
.getAttribute("errorResponse");
if (errorResponse != null)
throw new WebApplicationException(errorResponse);
return null;
}
}
Since the provider exists in JAX-RS land, the web application exception is processed according to the rules of Section 3.3.4 of the JAX-RS specification, and you get the desired response at the client side.
* EDIT:*
The bottom line is, there is no standard Java EE prescribed way (currently) to handle servlet exceptions in a centralized fashion similar to what is available in JAX-RS. Since you are using JBoss/RestEASY though, you could utilize the JBoss Seam Catch library to get pretty close.
#HandlesExceptions
public class ExceptionHandler {
public void handleServletException(
final #Handles #WebRequest CaughtException<ServletException> caught,
#Context final HttpServletResponse response) {
try {
response.sendError(500, "An error occured");
} catch (final IOException ioe) {
System.err.println("Dumb IO Exception: " + ioe);
}
}
}
The above illustrates an exception handler, as described in the Seam Catch documentation. Note that the library is in massive flux right now, so you will want to utilize it only as a last resort.
I've got this issue, recently I read about the REST arquitecture and it makes a perfect sense, so I'd like to achieve a RESTful web application.
Now, I'm following the Front Controller pattern that means that all of the URL mappings go to the controller.java servlet, I map the by specific URLs, not by using the /* wildcard,
the controller implements the four HTTP methods POST,GET,PUT,DELETE, each method calls the controllers service method and there I determine based on the HttpServletRequest and pathInfo the action to execute.
Controller.java
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
IAction action;
View view;
try {
action = ActionFactory.produceAction(req);
view = action.execute(req, resp);
switch (view.getDispatchMethod()) {
case REDIRECT:
resp.sendRedirect(resp.encodeURL(view.getResource()));
break;
case FORWARD:
req.getRequestDispatcher(view.getResource()).forward(req, resp);
break;
case INCLUDE:
req.getRequestDispatcher(view.getResource()).include(req,resp);
break;
default:
}
} catch (ActionFailedException uae) {
req.setAttribute("ActionName", "Action");
req.setAttribute("FailCause", uae.getMessage());
req.getRequestDispatcher(VIEW_FAIL.getResource()).forward(req, resp);
}
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
I've run into a particular issue when loading a specific order by the URI /orders/*, it is mapped to the controller servlet, the the action is executed and I load the appropriate order the action returns a View.java class
//ommited accessors and mutators for brevety.
public class View {
public enum DispatchMethod {
INCLUDE, FORWARD, REDIRECT
}
private DispatchMethod dispatchMethod;
private String resource;
public View(DispatchMethod dispatchMethod, String resource) {
this.dispatchMethod = dispatchMethod;
this.resource = resource;
}
}
Then the request is dispatched according to the getDispatchMethod() of the returned view.
Now, here is where the loop gets triggered, I use the following URL, myapp/orders/78965 /orders/* gets mapped to controller.java the appropriate action is executed and the correct order is found by the pathInfo() the returned view is new View(View.DispatchMethod.FORWARD,"order_details.jsp") the problem is that with the three available dispatch methods REDIRECT,FORWARD and INCLUDE a request is re-triggered on the URL and so on and on and on I never reach the order_details.jsp that renders the data.
So, how would you avoid the looping, as I'd like to preserve the URI displaying the order number I use the forward method, also, I'd like to do it using servlets, I've heard of the UrlRewriteFilter maybe in the future, but right now, how would it be done using "Plain Vanilla" since I'm using the Front Controller pattern, will it be necessary to add an additional servlet in the /orders/ URI ?
Any help or insights is truly appreciated.
EDIT 1:
Pasted the source code of the controller, a very basic one, I have my suspicions that the way the service method calls all of the overriden do[Method] of the servlet is triggering the loop and that it may be solved by splittig them.
Implementing a RESTful HTTP interface in Java is a lot easier using a JAX-RS implementation like RESTEasy or Jersey.
Using a Front Controller to dispatch requests to the right resource is a good approach, it's exactly the approach taken by these JAX-RS frameworks. I fear you may be re-inventing the wheel here by writing a bespoke URL parsing and dispatching mechanism when this can be taken off-the-shelf.
JAX-RS is a lightweight way to expose resources. By using a couple of simple annotations you can expose a REST interface without any plumbing required. For example:
public class Order {
#GET
#Path("/orders/{orderId}")
#Produces("text/html")
public void getOrder(#Context HttpServletResponse response,
#Context HttpServletRequest request,
#PathParam("orderId") String orderId) throws ServletException, IOException {
// ... create view and add to request here
request.getRequestDispatcher("orders.jsp").forward(request, response);
}
}
You can see how simple it is to attach this class to a URL path (using the #Path annotation), and how easily you can parse values from the URL using #PathParam. Since you get all the plumbing/dispatching/parsing off-the-shelf, you can concentrate on the bits of your app that are specific to your domain (such as what an order contains).