I'm trying to get my Spring 4 application to allow Path Parameters that end with an escaped slash.
The #RequestMapping I've got is:
#RequestMapping("/{externalSystemId}/{externalRequestId}/events")
And the URL that I'm calling with is /dummy/ab%2F/events. So "externalRequestId" is the one that has the trailing encoded slash.
I've gotten to the point where the handler is called correctly, and where escaped slashes in the middle of the path segment work - e.g. /dummy/ab%2Fcd/events, but not at the end.
What I've done so far:
Added system properties to Tomcat:
-Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true
-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
Custom extension of UrlPathHelper that replaces getServletPath with one that simply returns request.getRequestURI()
Configure the UrlPathHelper with:
urlDecode = false
removeSemicolonContent = false
Use this UrlPathHelper from mvc:path-matching, within mvc:annotation-driven
And I'm at a complete loss as to where to go next. I've even been debugging through both Spring and Tomcat to try and work it out, and I'm stumped. It looks like it's because UrlPathHelper.getPathWithinServletMapping() is calling request.getPathInfo(), and that is returning the path with the %2F stripped out. And that in turn is because somewhere else in Tomcat - I've lost the window now - it has decoded the %2F into a /, then replaced the "//" that is now present with a "/" instead.
This feels like it should be relatively straightforward, so what am I missing?
I've tried it on both Tomcat 7 and Tomcat 8, and with various versions of Spring 4 - currently on 4.3.14.
Problem solved.
The SlashFriendlyUrlPathHelper that I had that was the extension of UrlPathHelper was causing problems, because my webapp is not the Root webapp. Remove that, but keep every other change in place, and it starts to work properly.
Related
I'm using Spring Boot 2.4.6. For delete APIs getting 405 method not found. My endpoint is like: beauty/v1/sites/addressTemplates/:templateId
Path variable: ##$%#
Can someone please suggest what can be done to make this behavior as not complaining for 405? Please direct me to other questions in case I'm missing something.
I guess that your issue has nothing to do with Spring. Maybe you are trying to compose the whole URL by using reserved characters.
In a URL, a hash mark, number sign, or pound sign ( # ) points a browser to a specific spot in a page or website. It is used to separate the URI of an object from a fragment identifier. Source.
Which means that an URL which looks like:
beauty/v1/sites/addressTemplates/##$%#
is not exactly what you imagine it to be because # is interpreted in a special way. What you have to do is to percent encode the "special" path variable so it will look like this at the end:
beauty/v1/sites/addressTemplates/%23%40%24%25%23
Then Spring will not complain anymore and will resolve properly the endpoint.
In my application.yml for my spring boot application I have this property:
apiKey: ${SECRET_ADYEN_API_KEY:djhksy{f7dsadasb}
The problem is that the API key contains a forward curly brace, which messes up the parsing of the key in the app. It becomes ${SECRET_ADYEN_API_KEY:djhksy{f7dsadasb}, not simply djhksy{f7dsadasb which is the actual API key. How can I get around this? I tried escaping the { character with \ and \\ without luck.
Could you please try separating the default value (djhksy{f7dsadasb) to a different property just like below, It is working for me :
default-secret-key: djhksy{f7dsadasb
apiKey: ${SECRET_ADYEN_API_KEY:${default-secret-key}}
I have a SpEL expression that I am trying to use for an FtpMessageHandler (specifically for outputHandler.setRemoteDirectoryExpressionString("headers['" + ftpOutPath + "']");).
Through debugging I have found that this SpEL expression is not getting compiled. When compilation time comes, SpelCompilerMode is set to OFF. So as a result the expression is not being compiled and just returning null (and my FTP client is trying to just write to the root directory instead of the one I am specifying).
Looking at the Spring SpEL documentation (https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/core.html#expressions) , it says that in order to enable SpEL compilation you need to set the property spring.expression.compiler.mode. I have done this to no effect - I have set spring.expression.compiler.mode to immediate in my application.properties file, yet SpelCompilerMode is still OFF.
Why is this happening? How can I fix this?
I know someone will ask for a code sample but honestly I have no idea what I can provide as this is all stuff internal to Spring. But here are some samples of related code:
The FtpMessageHandler:
#Value("${my.ftp.outPath}")
private String ftpOutPath;
#Bean
#ServiceActivator(inputChannel = "myChannel")
public MessageHandler getOutputHandle(){
FtpMessageHandler outputHandler = new FtpMessageHandler(mySessionFactory());
outputHandler.setRemoteDirectoryExpressionString("headers['" + ftpOutPath + "']");
return outputHandler;
}
application.yaml
spring:
expression:
compiler:
mode: immediate
my:
ftp:
outPath: myPath
I still am not sure why the expression was not compiling / intrepreting properly, however changing the code to use LiteralExpression worked instead of using the String.
outputHandler.setRemoteDirectoryExpression(new LiteralExpression(ftpOutPath));
I am trying to incorporate Spring HATEOAS into an existing service tool and REST API. However, one of the issues I"m encountering is that the ControllerLinkBuilder seems to remove trailing slashes (which is a requirement due to existing constraints). Is there some workaround or method to get that trailing slash added to the link?
user.add(linkTo(UserController.class).slash("user").slash(user.getUsername() + "/").withRel("Get User Details"));
(User extends ResourceSupport)
I have also tried .slash("") to no avail.
The output:
"http://localhost:8080/REST/user/<username>"
Not just using link builder as far as i can see. LinkBuilders use UriComponents to parse it out. Then it goes through each segment and adds them back into the a UriComponents builder. stuff/ goes in the UriComponents but stuff is all that comes out.
What you can do is make that a Link in a round about way, eg:
user.add(
new Link(
linkTo(UserController.class).slash("user").slash(user.getUsername())
.toUriComponentsBuilder().build().toUriString() + "/",
"Get User Details"
)
);
there's probably an easier way...but you get the idea. Link doesn't seem to shove you through the UriComponents stuff
Suppose my Tomcat webapps directory looks like this:
webapps/
webapps/fooapp/
webapps/fooapp/WEB-INF/
webapps/fooapp/WEB-INF/web.xml
webapps/fooapp/bardir/
When I make a GET request for /fooapp/bardir, Tomcat sees that webapps/fooapp/bardir is a directory and sends back a 302 to /fooapp/bardir/ (with a slash at the end).
Here is my question: Where in the Tomcat source code does this take place? (I'm looking at 6.0.x but a correct answer for any version would be a great starting point.)
The only reference material I can find on this subject is in the Catalina Functional Specifications which states, regarding the Default Servlet:
On each HTTP GET request processed by this servlet, the following processing shall be performed:
[...]
If the requested resource is a directory:
If the request path does not end with "/", redirect to a corresponding path with "/" appended so that relative references in welcome files are resolved correctly.
However, this functionality does not appear to be in org.apache.catalina.servlets.DefaultServlet; or at least, it's not there exclusively: if I replace the default servlet in web.xml with a servlet whose servlet-class does not exist, directory paths still come back 302 to add the slash, while every other request comes back with an error as expected.
I think it happens in org.apache.tomcat.util.http.mapper.Mapper, namely in the internalMapWrapper (Context, CharChunk, MappingData) method.
But unfortunately I'm not really sure -- maybe this really is a question better suited for the tomcat-users mailing list. Sorry for not having a better answer.
The Eclipse debugger learnt me that the redirect happens in line 504 of CoyoteAdapter class, almost in the end of the postParseRequest() method.
// Possible redirect
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
if (!redirectPathMB.isNull()) {
// ...
response.sendRedirect(redirectPath); // <--- Here.
return false;
}
Tomcat 6.0.20 btw.
Update: actually, the redirectPath is indeed filled by the Mapper as mentioned in #Henning's answer, indeed in the internalMapWrapper() method. Checkout the source code here.
if(mappingData.wrapper == null && noServletPath) {
// The path is empty, redirect to "/"
mappingData.redirectPath.setChars
(path.getBuffer(), pathOffset, pathEnd);
path.setEnd(pathEnd - 1);
return;
}