Static path URL is not getting priority over RequestMapping Url - spring

My application is hosted on https://somedomain.com/app-name
I have defined RequestMapping as "/app-name/**" as user can hit:
https://somedomain.com/app-name
https://somedomain.com/app-name/some-url
https://somedomain.com/app-name/some-other-url
https://somedomain.com/app-name/some-url/some-sub-url
So I need the rest of the path (which can contain / also)
RequestMapping as /app-name/** is solving my problem but then my static content doesn't get served as static-path-pattern: /app-name/static/**
Why Request mapping is getting priority over static-path-pattern? How can I resolve this?

If possible in your application, Try changing static-path-pattern spring.mvc.static-path-pattern=/resources/**
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content

Related

How to get the current Request Mapping URL configured at Controller layer when request is executed?

I went through so many links like How to show all controllers and mappings in a view and How to configure a default #RestController URI prefix for all controllers? and so on.
I want to get the Request Mapping URL at Filter interceptor
Ex: This URL I configured at REST controller method, and naturally we will pass /employees/employee-names/John to get the Employee John.
/employees/employee-names/{employee_name}
Now, when somebody hit /employees/employee-names/John I want to get the value of actual mapping url if REST controller /employees/employee-names/{employee_name},
Any pointers how to get that ?
Spring MVC sets the attribute HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, which you can use to get the pattern that was used to match the incoming request:
String matchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
That would return /employees/employee-names/{employee_name} in your case.
I was able to solve this issue using below code. AntPathMatcher is the perfect way to identify if the incoming request and URL you configured in the property file matches exactly. This solution works greatly for me.
AntPathMatcher springMatcher = new AntPathMatcher();
Optional<String> antMatch = props.getMapping().stream()
.filter(//Perform Some Filter as per need)
.map(Mapping::getVersion)
.findFirst();
return antMatch.isPresent() ? antMatch.get() : null;

Influencing order of RequestMapping processing

In short this is what I'm searching for: I want to create a RequestMapping that catches all URLs except a small list I don't want to catch.
The minimum to exclude is everything below /webjars/, I also would like to exclude other URLS like /actuator/ and probably more.
Background information
We need to replace an old CMS which has literally thousands of different URLs. All URLs need to be detected and checked against a database and then the users shall be presented with a landing page, this landing page will then redirect the user to the new CMS target URL.
The logic that needs to be applied is too complicated for some Apache / nginx magic, therefore I wrote a Spring Boot application that can accomplish this.
I've created a #RequestMapping(value = "**", method = RequestMethod.GET) that catches all GET requests (these are the one I want to grab and react on) and a #RequestMapping(value = "**") for the other http verbs like POST and DELETE which I simply abort by sending a http error status code.
This works fine and the (simplified) code looks like this:
#RequestMapping(value = "**", method = RequestMethod.GET)
public String catchAccepted(HttpServletRequest request, HttpServletResponse httpServletResponse, Model model) {
model.addAttribute("targetUrl", ua.deriveNewUrl(request));
return "redirect";
}
#RequestMapping(value = "**")
#ResponseBody
public ResponseEntity<Object> catchDenied(HttpServletRequest request, HttpServletResponse httpServletResponse) {
return new ResponseEntity<Object>(HttpStatus.I_AM_A_TEAPOT);
}
The page that gets displayed for all the GET requests is based on a Thymeleaf template which uses Bootstrap in order to do the layout job.
In order to include Bootstrap I use the webjars-locator and org.webjars.bootstrap, the resources are included by specifying <script src="/webjars/bootstrap/js/bootstrap.min.js"></script> in the redirect.html Thymeleaf template.
Problem
The problem is, that my ** mapping on GET also gets applied to the /webjars/... call and instead of the desired js code I get whatever my redirect template returns.
I found no way to tell Spring Boot about the desired order.
First I would like to have Spring Boot handle the webjars mapping, then my other mapping.
Attempts so far
I checked other posts on SO but they only work when I have access to the sourcecode where the mapping is made. And I don't have access to the webjars locator / see no point in changing it just to solve this issue.
I also tried to set up a "anything that is not related to webjars" mapping like this:
#RequestMapping(value = "^(?!webjars$|actuator$).*", method = RequestMethod.GET)
But this doesn't have the desired effect because the RequestMapping only seems to support ant-stlye paths, which doesn't support negations because Ant (in contrast to Spring Boot) has a field for includes and excludes: https://ant.apache.org/manual/dirtasks.html
Negating the mapping seems only to be possible for params, not for the path: Change #RequestMapping order
I didn't yet find a way to influence the order if other mappings come from code I can not incluence.
But I found a way to configure "catch all except of ...":
#RequestMapping(value = { "", "/", "{url:(?!webjars$|actuator$).*}/**" }, method = RequestMethod.GET)
This configures three mappings. The first two are there to handle calls to the root of the webserver. The third configures a path pariable which I could also put into a #PathVariable but in my scenario the value doesn't matter. If you configure a path variable you need to give it a default because the pattern will only be satisfied depending on the value of your URL.
The regex tells Spring Boot only to react if the url doesn't contain webjars or actuator. The regex itself is best explained by using regex101:

Spring MVC RequestMapping with i18n path parts

What are the approaches to map i18n translated url paths?
For example lets say we talk about the follwing url (for locale en):
www.foo.tld/car/manufacturer
In german (de) this url would be
www.foo.tld/auto/hersteller
What i know about Controller RequestMapping i could use severel values to map these url's for one method like
#GetMapping(value={"/car/manufacturer/", "/auto/hersteller/"})
More seo optimized would be probably something with the current locale in the path like
#GetMapping(value={"/en/car/manufacturer/", "/de/auto/hersteller/"})
...but i dont want to start a discussion what would be the best uri seo wise.
This isn't so bad if to use only a few Locales/Languages but i would like to make this somehow dynamic.
Currently im using messages_xx.properties to map url path parts for generating urls in my application, like:
messages.properties
uri.car=car
uri.manufacturer=manufacturer
messages_de.properties
uri.car=auto
uri.manufacturer=hersteller
Im using them already for building links repecting the user locale which works fine.
What im searching now for is a elegant, less error prune way to map these urls in my controller. If i would change for example a value for the key uri.car and would have a static RequestMapping in my controller like in the example above i also need to change it there (if i dont forget!).
Also if i would like to add support for another language i would need to search in all controller and check if i need to add another value mapping.
Is there a smarter way how to map i18n path parts in Spring controllers, ideally respecting a request locale and resolve the path string with the help of messages_xx.properties?
Or would be a filter the way to go extracting path parts according to the requested locale and use internally only one language for mapping urls?
Supose that you have an EN message.properties and a DE message.properties with the following property:
url.car=/car/manufacturer/ in the EN
url.car=/auto/hersteller/ in the DE
In your #Controller you can get easily this properties configuring your messageSource and using it to get the properties:
Inject your configured MessageSource to allow Spring resolve the messages:
#Autowired
private MessageSource messageSource;
Then you can get all the properties from your message.properties file:
String url= messageSource.getMessage("url.car", put_here_your_locale);

Get current ApplicationPath value in Jersey resource method

I have a jersey 2.0 application using mcv with freemarker templates. In one template I have a form whose action is to resubmit to same url. Say the form url is:
http://my-domain.com/app-base-path/my-form
So the application annotaion is : #ApplicationPath("app-base-path")
and resource path annotation is #Path("my-form"). Great.
I'm trying to set the form action dynamically to be:
<form name="settings" action="${model.formAction}" method="post">
where action should equal: app-base-path/my-form
I'm trying to set the value in the resource by injecting UriInfo. This is what I'm getting:
formAction = uriInfo.getPath();
//result formAction = "my-form"
How can I retrieve the path including app-base-path?
No javascript please!
You can get the absolute URI with uriInfo.getAbsolutePath(), which will return a URI with the full URI of the corresponding resource method.
http://localhost:8080/app-base-path/myform
A URI can be broken down in distinct parts, which the URI class has specific methods to obtain those parts
scheme authority path
--------------------------------------------------------
http :// localhost:8080 /app-base-path/myform
The URI class has method to obtain all of those.
uri.getScheme()
uri.getAuthority()
uri.getPath()
I'm pretty sure I don't need to tell you which one you want :-)

rest url - having '/' as a path VALUE

I think this is not possible, but I need confirmation before throwing it away...
I have a GET REST endpoint with this pattern
/networks/{networkId}/publishers/{publisherId}/ratings
the problem I am facing is, publisherId could have '/' in its id, like the id could be "opt/foo/bar" (we have not control over this id, it is given to us by our clients).
So
/networks/68/publishers/opt/foo/bar/ratings - obviously does not work, getting a url not fond error.
/networks/68/publishers/opt%2ffoo%2fbar/ratings - also does not work. same error.
I know passing it as a query param will work. But I want to know if there is a way to make it work having it as a path param?
Thanks!
URL encoding is the right way to go but it looks like your container is decoding the slash before Jersey receives it.
Assuming you are using Tomcat, you can attempt to persuade Tomcat to allow the encoding, try:
tomcat/bin/setenv.bat
set
CATALINA_OPTS="-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
I don't know if other containers have similar issues and settings.
I have not tried this, but theoretically this should work in Jersey:
#Path("/networks/{networkId}/publishers/")
#GET
public String get(#PathParam("networkId") String networkId, #Context UriInfo ui) {
java.util.List<PathSegment> segments = ui.getPathSegments();
// Last segment is "ratings", the rest is your publisherId.
}

Resources