Spring MVC #PathVariable to catch only first level - spring

I have a Spring Boot app that includes some controllers and static resources. I need to be able to have a controller that matches:
/hello
and
/hello/
but not
/wonder/hello
(or anything else). It seems that when I use the following mapping:
#RequestMapping(value = "/{slug}", method = RequestMethod.GET)
public String mapping(#PathVariable("slug") String slug)
it does a "catch-all" whereas I only need it to catch the first level. This causes issues with the static resource mapping.

use #RequestMapping("/hello") in the starting of controller

Related

Feign Client with Spring Boot: RequestParam.value() was empty on parameter 0

I created a simple Feign Client with Spring Boot like this:
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam String name);
}
But when I try just to start an application I get an error:
java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
First I didn't understand what is the reason and googled a lot but didn't find an answer. Almost excidentely I figured out that it works if to write request param name explicitly:
#RequestParam("name") String name
So my question: is it a bug or could it be configured to not write request params names explicitly?
Both Spring MVC and Spring cloud feign are using the same ParameterNameDiscoverer - named DefaultParameterNameDiscoverer to find parameter name. It tries to find the parameter names with the following step.
First, it uses StandardReflectionParameterNameDiscoverer. It tries to find the variable name with reflection. It is only possible when your classes are compiled with -parameters.
Second, if it fails, it uses LocalVariableTableParameterNameDiscoverer. It tries to find the variable name from the debugging info in the class file with ASM libraries.
The difference between Spring MVC and Feign occurs here. Feign uses above annotations (like #RequestParam) on methods of Java interfaces. But, we use these on methods of Java classes when using Spring MVC. Unfortunately, javac compiler omits the debug information of parameter name from class file for java interfaces. That's why feign fails to find parameter name without -parameter.
Namely, if you compile your code with -parameters, both Spring MVC and Feign will succeed to acquire parameter names. But if you compile without -parameters, only Spring MVC will succeed.
As a result, it's not a bug. it's a limitation of Feign at this moment as I think.
Just use String greeting(#RequestParam("name") String name);
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam("name") String name);
}
I use upgrade maven-compiler-plugin to solve this plobrem. you can access: https://blog.51cto.com/thinklili/2566864
This worked for me.
#FeignClient(name="session-service", url="${session.host}")
public interface SrocessingProxy {
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload);
}
//Service
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload) {
System.out.print("Key : " + key);
}

Spring MVC - #PathVariable -Failed to find resource

In my Spring MVC demo project I am using path variable annotation.
Controller looks like below.
#Controller
#RequestMapping(value = {"/", "/login"})
public class HelloWorldController {
#RequestMapping(method = RequestMethod.GET)
public String helloWorld(final Model model) {
model.addAttribute("message", "Hello World!");
System.out.println("hello world");
return "jsp/login.jsp";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getLoginId(#PathVariable final int id, final Model model) {
model.addAttribute("message", "Hello World!");
System.out.println("hello world 2" + id);
return "jsp/login.jsp";
}
}
Now when I trying to access the below url
http://localhost:9080/ExampleSpring/login
the controller is going to login.jsp page. This is an expected result.
but when the url is changed to
http://localhost:9080/ExampleSpring/login/9
I am getting the below error and java.io.FileNotFoundException: JSPG0036E: Failed to find resource /login/jsp/login.jsp
Could anybody please tell me the reason behind this?
Actually I guess this could a reason and solution .. I guess for the former case ie. http://localhost:9080/ExampleSpring/login case it tries to look for the resource after the context root that is inside WEB-INF. But in the later case as it is ecountered a second / after /login it tries to find the resource inside WEB-INF/login folder which actually does not exists, thus the error came.
Soulution could be , to use "../jsp/login.jsp" which will force to check the resource from the context root. What ever long request string u can pass this will ensure that the resources are searched from the context root.
May be thats why to avoid this confusion spring has come up with View Resolver.
Please correct me if I am wrong.
Thanks all.
If I correctly understand you comments, you have no configured view resolver, so you simply forward to the view passed as view name.
As you have no initial / you fall in the hell of relative URLs. In that case, you should write :
return "/jsp/login.jsp";
if the view is at /WEB-INF/jsp/login.jsp
BTW, the reason for ViewResolvers is (among others such as easy refactoring) to do that low level work of adding /jsp/ as prefix and .jsp as suffix. So IMHO, you really should considere to add one to your config.
I think you should put :
....#PathVariable("id") int id ....

Is there an equivalent of a "beforeHandler" for spring 4?

I have a controller with a requestmapping..
#Controller
public class TestController {
private static final String template = "Hello there, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/hello")
public #ResponseBody String hello() {
return "Hello";
}
}
How can I make it such that everytime a user goes to a RequestMapping, or whichever url, some other method is called to println to the console the URL the user is at before it actually enters the method "hello"?
I would like to use this method to ensure users have proper credentials in the future. I see there are #PreAuthorize annotation, but there doesnt seem to be a method associated with it that I can write my own logic, i.e. do a simple println to the console with the current URL the user is at.
You have a number of options.
With Spring, you can implement and register a HandlerInterceptor and implement its preHandle method to log the request URL, which you can reconstruct with the various HttpServletRequest methods.
With pure servlet-api, you can implement and register your own Filter which logs the request URL and then continues the chain, with doFilter(..).
You can also use AOP, advise all your #RequestMapping annotated methods with a #Before advice that logs the URL.

Spring MVC Url Pattern Issue

I have an existing application ..where we have some jsp..in view folder of the
application Context.Now we need to migrate one of the JSP's to a spring mvc controller..
I was doing a sample application and this is what i observed...
if I have the pattern in web-xml as / and annotate the controller as
RequestMapping(value="/view/Track.jsp")
This doesnt work..but If I modify it as /view and
update it as
RequestMapping(value="Track.jsp") then it works..
What is the reason for this..and what should be the approach for the migration.
Spring MVC concatenates the mapping from the controller and the method. With your second mapping it results in: /view/Track.jsp - this is OK. When the mapping on th controller is /view/Track.jsp you probably don't have mapping on the method, right? That way spring don't know which method of the controller to call even if you got only one...
If you want to use only the mapping on the controller you can place #RequestMapping(method = RequestMethod.GET) on the method.
#Controller
#RequestMapping("/view/Track.jsp")
public class UserController {
#RequestMapping(method = RequestMethod.GET)
public String track( MapModel model ) {...}
}
Good practice is to use the mapping on the controller to "modulaize" your mappings if you got more controllers. That way you will prevent collision on the method mappings between controllers.

Spring URL mapping conflicts

At the moment I am bussy with implementing a new url structure for our webshop. The new url structure should be more optimized for search engines. We also want that our old structure will still be working and will use a 301 to redirect to a the new structure.
The problem is: the new structure sometimes conflicts with the old urls.
Example of the old url mapping:
#RequestMapping(value = "/brand/{categoryCode}/{categoryName}/{brandGroup}.do", method = RequestMethod.GET)
New structure:
#RequestMapping(value = "/brand/{brandGroup}/{superCategoryName}/{categoryName}.do", method = RequestMethod.GET)
As you can see the url's have the same amount of values, so the old mapping will catch the new one and vice versa.
What is the best way to fix this? Using a url filter to rewrite the old ones to the new url structure?
You could use an URL router in Spring MVC; you can define conflicting routes within your app and handle them with route prorities (first route to match the request wins) and refine request matching.
Your routes configuration file could look like:
GET /brand/{<[0-9]+>categoryCode}/{categoryName}/{brandGroup}.do oldcontroller.oldAction
GET /brand/{<[a-zA-Z]+>brandGroup}/{superCategoryName}/{categoryName}.do newController.newAction
In spring boot, regular expressions can be used when mapping the #PathVariable, and this can be useful to resolve url conflicts:
#RestController
public class TestController {
#PutMapping("/test/{id:^[1-9][0-9]*}") // id must be a number greater that 1
public void method1(#PathVariable long id, #RequestBody DataDto1 data) {
}
#PutMapping("/test/foo")
public void method1(#Valid #RequestBody DataDto2 data) {
}
}

Resources