How do I "capture" the part of the path matched by ** - spring

I've got a #RequestMapping that looks like:
#RequestMapping("/foo/{bar}/blah/**")
public void handleRequest(#PathVariable String bar, #PathVariable String remainder) {
How do I configure things so that remainder gets whatever is matched by "**"?

While Spring does support Ant style path patterns, they have not provided a way to return what was captured.
Fear not though, as this is something you can calculate yourself. You can define your controller method to take in the HttpServletRequest object. From there, you are able to access the URI by calling getRequestURI() on the request object.
It will require some String processing, but that just sounds like more fun code to write.

Related

Spring Controller Mapping value regex

I have a spring controller method
#GetMapping(value = {"{name}"})
public String index(ModelMap model, #PathVariable Optional<String> name)
I would like everything to go through this method except anything that contains a period. So something like /main.css will be rejected while something like /maincss will be allowed. Is there any way I can do that?
I understand that I can simply move all my resources file to resources path like /resources/main.css and the problem is fixed, or I can change up the code so that I have two methods, one for / and one for /name/{name}. But for educational purposes I was wondering if I can have controller apply to everything except a certain regex. Like for example what if I want the controller to accept everything except a string that contains a word Norris.
Is there anyway to apply regex or some sort of rules to mapping value?
You can use the format varName:regex (where colon ":" is the separator between the two) to indicate a regex check to be used on the variable:
#GetMapping(value = {"{name:^((?!Norris).)*$}"})
public String index(ModelMap model, #PathVariable Optional<String> name)
However, since your problem might be related to resources:
I understand that I can simply move all my resources file to resources path like /resources/main.css
You could also try to configure a ResourceHandlerRegistry or a ResourceResolver to exclude certain file (patterns) from being served as resources. More info regarding resource handling can be found here.

Spring controller method invocation advice

I have a controller that exposes the following endpoint:
#RequestMapping("/all-users")
List<User> getAllUsers() {
...
}
I have also an annotation that helps me out with versioning of those endpoints, which ends up on something like this:
#RequestMapping("/all-users")
#Version(version=1, latests=LATEST_ALL_USERS)
List<User> getAllUsers() {
...
}
Now I want to introduce an additional standard behavior to all handlers mapped wish method contains #Version annotation which will simply wrap the response object into another object which contains the current version and latest version of the invoked method. Some information to build this object are provided by #PathVariable parameters. I'm trying to find a hook that allows me that but no luck so far.
I tried first to have a custom RequestResponseBodyMethodProcessor but if I add it will not take any effect because the original RequestResponseBodyMethodProcessor comes before and I don't want to remove the ResponseBody from my endpoints.
Afterward I tried to go for the mapping instead, once I cannot handle it on the processor, maybe I could handle that on mapping time introducing my code pre and post method invocation, but got stuck on the point where mapping is registered where a method object is needed, not allowing me to introduce my advice code.
Is there any way to get this done?
Edit:
Some of the information needed to build the new returned object are provided as #PathVariables, and are available on end-point method call.

How can I map URL containing colon to my Spring controller?

I want to map a URL (for example, http://example.com/v1/books:search) containing colons to my Spring MVC controller, but I can't make it work.
#RequestMapping("/v1/books")
public class BooksController {
#GetMapping(":search")
public Page<Book> search(#RequestParam String author) {
// Return books written by the author.
}
When I test this API, Spring returns 404 NOT_FOUND to me. It seems that Spring doesn't support colons in URL mapping.
Is there any method to make it work? Thanks.
I hit this attempting to do similar so I thought I'd share my findings.
With using most defaults and your code, the search method will be mapped to /v1/books/:search which is obviously not quite what you want. There are two places that I've found so far that get in the way of changing this. The first is the AntPathMatcher's combine method. This method will attempt to put a path separator (/) between segments. The second place is within the RequestMappingInfo's path parsing code. The former can be replaced easily. The latter not so much.
As the methods that tend to be problematic involve combining multiple #RequestMapping annotations, what I've found to work is to simply side-step combinations. On my controller class, I have a #Controller annotation and any defaults for #RequestMapping, but not a path attribute. On each method, the full path is then added. This isn't great, but it does get collection-level special "methods" to function properly. In your example, this would look like:
#Controller
#RequestMapping
public class BooksController {
#GetMapping("/v1/books:search")
public Page<Book> search(#RequestParam String author) {
// Return books written by the author.
}
Long story short: Do not do this - use / as a separator for the method.
A bit more detail: Have a look at Spring Framework issue #24771 that suggests that the team actually moves away from various ways to handle non-standard URL mappings in favor of simpler logic of URL processing, after entangling in a series of various issues with similar concepts. This "custom method" thing is unlikely to get a first class support in Spring, as a result.
Therefore, despite what Google does, just do this as a normal person and use /v1/books/search path:
#RequestMapping("v1/books")
public class BooksController {
#GetMapping("search")
public Page<Book> search(#RequestParam String author) {
// Return books written by the author.
}
}

Using #Value annotation with static final variable in Spring Framework

I want to make the Request Mappings in my Spring application dynamic. So that my url can not be understandable. And I can show anything meaningless to the user and still mapping purpose will be resolved.
For that I am storing the dynamic part of the URL in properties file. And want to use that in the #RequestMapping annotation. And the same thing will be done on the client side in JSP. I will read the value from the property file and then create the href.
I am using #Value annotation to read the property file values.
There is one class that holds all such values in final static variables.
public class UrlMappingAbstraction {
public static final #Value("#{urlAbstractionProperties['url.message']?:'message'}") String MESSAGE = "";
}
And I am extending this class in my controller and using the static final field in the #RequestMapping annotation like below.
#RequestMapping(value="/"+MESSAGE+"/{id}", method=RequestMethod.GET)
And in jsp also I am reading the value from property file using <spring:message/> and generating the url in href.
The problem is jsp able to create the correct url based on the property file value but in the #RequestMapping annotation my value is not getting replaced.
Can anybody tell me the exact problem? I know that we can not change the value of static final variable after its initialized. Then what's the use of #Value annotation.
If this can be done another way then you can also show me it.
Thanks in advance.
Annotations are static by their nature, therefore you cannot do it this way.
#Value cannot be used on static fields, but it doesn't matter here - the real problem is that there is no way to use values other than compile time constants as attributes of annotations.
You can use one of the following alternatives:
Add a URL rewrite filter (such as this or this) and configure it to perform the necessary conversion.
This approach looks more elegant due to clear separation of responsibilities - controllers are responsible for doing their jobs, rewrite filter is responsible for obfuscation of URLs.
Intercept creation of controller mappings by overriding RequestMappingHandlerMapping. getMappingForMethod() and change their URL patterns at this step (not tested)
I will augment #axtavt's suggestions by saying you should just do it in reverse. Do you really need to make the message URL runtime configurable?
If you don't than just make a static variable just like you have it but with out the #Value:
public final class UrlMapping {
public static final String MESSAGE = "message";
}
Then in your JSP refer to UrlMapping.MESSAGE instead of the properties file.
Although its not as flexible its far simpler and IMHO its a bad idea to make endpoint URLs too configurable because inevitably you will hardcode something either in Javascript or in a template. Also changing URLs are bad for SEO.
You can follow this approach
#Value("${name}")
private String name;
private static String NAME_STATIC;
#Value("${name}")
public void setNameStatic(String name){
PropertyController.NAME_STATIC = name;
}
src - https://www.baeldung.com/spring-inject-static-field

Spring MVC request / command objects

Is there a convenient way to turn something like this..
#RequestMapping("/some/action/{q}/...")
public void doSomething(#PathVariable("q"), oh, my, god, a, billion, annotated parameters) { .. }
Into something like this..
#RequestMapping("/some/action/{q}/...")
public void doSomething(NiceEncapsulatingRequetObject request) { .. }
With Spring MVC?
After checking the docs, it doesn't seem like it is supported out of the box. You could try to create your own HandlerMethodArgumentResolver which gives you this feature. You might run into some issues since you'll need a circular reference between your implementation and the HandlerMethodArgumentResolverComposite instance. Nevertheless I think it should be possible.
Yes spring supports this out of the box, it is usualy refered to as bean binding.
Basicly you create an object with paramaters with the same name,
so if you have a paramater "q", your object should contain a private string q with both getter and setter present. It's also prefered not to use any constructors.
Spring will just fill in the paramaters it has in your object and pass it via the method's paramater.
You can create you own object like NiceEncapsulatingRequetObject and it's attributes should be String oh, Integer my etc. If you send the request with the exact names it will work

Resources