Unable to exclude URL's in the servlet mapping in Spring MVC - spring

I have a controller in Spring MVC 3.2 which I want to allow the follow url's:
http://localhost:8080/mypage
http://localhost:8080/mypage/
http://localhost:8080/mypage/foo
I want to exclude anything else i.e. http://localhost:8080/mypage/bar should result in an error. Currently bar gets mapped to the getStuff method.
I assume I must be able to accomplish this without using a filter?
My servlet mapping looks like this:
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/mypage/*</url-pattern>
</servlet-mapping>
The controller request mapping:
#RequestMapping(method = RequestMethod.GET)
public String getView(HttpServletRequest request, #ModelAttribute("myform") final MyForm form) {
return "myview";
}
#ResponseBody
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#POST
#RequestMapping(value = "/save")
public String onSave(#RequestBody MyForm form, HttpServletRequest request, HttpServletResponse response) {
return "saved";
}

You have to use the Spring MVC filters called Interceptors.
If you write your own Interceptor you have two options as below:
1. option:
Use the postHandle() method to check your conditions. If you use the postHandle() method you have access to the ModelAndView and if your url doesn't match with one of your 3 url's you just can throw an Exception or change the ModelAndView.
2. option:
Use the preHandle() method, check the url's and if they don't match throw a ModelAndViewDefiningException with a new ModelAndView() like this:
ModelAndViewDefiningException(new ModelAndView("errorPage"))

The <url-pattern> Tag of servlet does not support exclusions. This is a limitation of the Servlet specification.
You have to create this functionality programmatically in a filter.

Related

how to Enforce request header on all spring web RestController equests

Is there an option to specify a request header once in spring web RestController instead of doing it on every request?
e.q.
#RestController("workflowController")
public class MyClass{
public Value list(#RequestHeader(USER_ID_HEADER_PARAM) String user) {
...some code
}
public Workflow create(#RequestBody Workflow workflow, #RequestHeader(USER_ID_HEADER_PARAM) String user) {
... some code
}
}
the #RequestHeader(USER_ID_HEADER_PARAM) will be repeated in every request.
is there a way to specity it in the #RestCotroller level or the class level?
Thanks
Use some kind of filter class that can be configured to wrap around your requests in your servlets based on the URL path.
Here is info about the generic Servlet API filter API:
https://www.oracle.com/technetwork/java/filters-137243.html
If you're using Spring, there's another way to do it:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#filters
https://www.baeldung.com/intercepting-filter-pattern-in-java

Spring MVC URL redirect before controller request mappings are called

I'm using Spring 3.2.8. I get a mapping from a URL p to a URL q from an external API. The mapping changes at runtime. I want to redirect (302) the URL before the controller request mappings are called. How can I do that?
Thanks!
I have a solution. A default controller mapping for every RequestMethod.GET request is called if the requested URL does not exist. You have also to load static content (images, css, js, …) by your servlet container without touching Spring.
#Controller
public class DynamicURIForwardController {
#RequestMapping(method = RequestMethod.GET)
public String forward(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
// Stuff comes here.
// final String forwardURI = ...
return "forward:" + forwardURI;
}
}

RequestMapping in spring with weird patterns

I have defined the controller method in spring like following
#RequestMapping(value = "/register", method = RequestMethod.GET)
public ModelAndView register(HttpSession session) {
and when i try to access the register page it is working fine.
localhost:8080/myapp/register
but the issue is it is giving me the same page with all these patterns
localhost:8080/myapp/register.htm
localhost:8080/myapp/register.abc
localhost:8080/myapp/register.pqrasdfdadf
i want to stop this behaviour. can anyone suggest ?
Depending on how you do your Web MVC configuration, you have to set the RequestMappingHandlerMapping useSuffixPatternMatch property to false.
If you're doing your configuration through a WebMvcConfigurationSupport sub class, simply do
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping();
mapping.setUseSuffixPatternMatch(false);
return mapping;
}
With XML configuration, you'll need to explicitly declare a RequestMappingHandlerMapping with id requestMappingHandlerMapping and set the corresponding <property>.
This property, which is true by default, determines
Whether to use suffix pattern match (".*") when matching patterns to
requests. If enabled a method mapped to "/users" also matches to
"/users.*".
I think if you use #RequestMapping(value = "/register/", method = RequestMethod.GET) , you can easily solve your problem.
Another Solution:
Use regular expression.
#RequestMapping(value = "{varName:/register$}")
Here $ represents the end of the string.

How to configure which controllers Spring #ControllerAdvice will be applied to?

I have two types of controllers in my spring application.
View controllers that forward to views to generate HTML
API controllers that return JSON directly from the controllers
Both the API and View controllers are part of the same spring dispatcher servlet. Spring 3.2 introduced the #ControllerAdvice annotation to allow for a global location to handle exception.
The documentation implies that #ControllerAdvice will be applied to every controller associated with a Dispatcher Servlet.
Is there a way to configure which controllers #ControllerAdvice will apply to?
For example in my scenario I want a #ControllerAdvice for my View Controllers and separate #ControllerAdvice for my API controllers.
For people that will still find this question:
As from Spring 4 ControlerAdvice's can be limited to Controler's with the specified annotations. Take a look at:
http://blog.codeleak.pl/2013/11/controlleradvice-improvements-in-spring.html
(second half of this article) for more details.
UPDATE
I am using spring 4. You can do one of 2 below options.
(1) You can add the packages you want. (Inside those packages you have controllers that you want to follow #ControllerAdvice).
Ex:
#ControllerAdvice(basePackages={"my.pkg.a", "my.pkg.b"})
(2) You can directly add the controller classes you want.
Ex:
#ControllerAdvice(basePackageClasses={MyControllerA.class, MyControllerB.class})
I do not think this is possible now. If you can make the API and View controllers throw different Exception types, then you could define two different #ExceptionHandlers and achieve what you want.
// For handling API Exceptions
#ExceptionHandler(APIException.class) // Single API Exception
#ExceptionHandler({APIException.class, ..., ,,,}) // Multiple API Exceptions
// For handling View Exceptions
#ExceptionHandler(ViewException.class) // Single View Exception
#ExceptionHandler({ViewException.class, ..., ...}) // Multiple View Exceptions
You could use aop to translate the Exceptions coming out of APIs to a standard APIException. See this thread on spring forums.
Hope it helps.
Your exceptions should not dictate the content-type of your response. Instead check the request's Accept header for what the browser expects.
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {
...
String header = request.getHeader("Accept");
if(supportsJsonResponse(header)) {
//return response as JSON
response.setContentType(JSON_MEDIA_TYPE.toString());
return Json.stringify(responseMap);
} else {
//return as HTML
response.setContentType("text/html");
}
#ExceptionHandler(value=Exception.class)
public ModelAndView error(Exception ex) {
return new ModelAndView("redirect:/error/m");
}
...//ErrorController
#RequestMapping(value = "/m", produces="text/html")
public ModelAndView error()...
#RequestMapping(value = "/m", produces="application/json")
#ResponseBody
public Map errorJson()...

Mapping same URL to different controllers in spring based on query parameter

I'm using spring annotation based controller. I want my URL /user/messages to map to some controller a if query parameter tag is present otherwise to some different controller b. This is required because when parameter tag is present then some more parameters can be present along with that which i want to handle in different controller to keep the implementation clean.Is there any way to do this in spring. Also is there any other elegant solution to this problem ?
You can use the params attribute of the #RequestMapping annotation to select an controller method depending on Http parameters.
See this example:
#RequestMapping(params = "form", method = RequestMethod.GET)
public ModelAndView createForm() {
...
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView list() {
...
}
It is a REST style like Spring ROO uses: if the request contains the parameter forms then the createForm handler is used, if not the list method is used.
If you want to go the Spring route, you can checkout the HandlerInterceptor mentioned here. The Interceptor can take a look at your query param and redirect the link to something else that can be caught by another SimpleUrlMapper.
The other way is to send it to a single controller and let the controller forward to another action if the query parameter is "b".

Resources