RequestMapping with slashes and dot - java

I have to support following URL format
/service/country/city/addr1/addr2/xyz.atom
/service/country/city/addr1/addr2/addr3/xyz.atom
where country and city can be mapped to #PathVariable but after that the path can be dynamic with multiple slashes. The end part will have .atom or similar.
I tried following, but none of the options seem to be working
Wildcard
#RequestMapping(value="/service/{country}/{city}/**")
Regex
#RequestMapping(value="/service/{country}/{city}/{addr:.+}")
UseSuffixPatternMatch
Override method in Config class
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
Looks like combination of slash and dots don't work with above solutions.
I keep getting 406 for non-matching Accept header, or 404

The most dynamic approach would be to use MatrixVariable to manage the list of addresses but it is not applicable in your context since the paths cannot be modified as far as I understand from your question.
The best thing you can do to manage your dynamic path is to proceed in two steps:
Set a RequestMapping that extracts all the data except the addresses
Extract the addresses manually in the method
So for the first step you will have something like that:
#RequestMapping(value="/service/{country}/{city}/**/{file}.atom")
public String service(#PathVariable String country,
#PathVariable String city, #PathVariable String file,
HttpServletRequest request, Model model) {
This mapping matchs with all the required paths and allows to extract the country, the city and the file name.
In the second step we will use what has been extracted to get the addresses by doing something like this:
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
path = path.substring(String.format("/service/%s/%s/", country, city).length(),
path.length() - String.format("%s.atom", file).length());
String[] addrs = path.split("/");
First we extract from the request the full path
Then we remove what we have already extracted which are here the beginning and the end of the path
Then finally we use String.split to extract all the addresses
At this level you have everything you need.

Can you try this,
#RequestMapping(value="/service/{country}/{city}/{addr:[a-z]+\\\\.(atom|otherExtensions)}")
Just have to specify the complete regex format wherein you are expecting an extension at the end of the url such as atom, since this will be interpreted by default as MediaType by Spring.
another solution is specify the accepted MediaTypes
#RequestMapping(value="/service/{country}/{city}/{addr}", consumes = {MediaType.ATOM, MediaType...})
You can create custom MediaTypes if it is not predefined in Spring.

Related

How do I parse an HttpExchange request after an ending slash?

I have a request as follows:
localhost:8000/location/:01
My code takes as input an HttpContext request.
func(HttpExchange r) {
String area_path = r.getRequestURI(); // Equals string "/location/"
}
How do I parse an HttpExchange correctly so I can pull out the "01" from this path and store it as a variable?
That (localhost:8000/location/:01) is not a valid URL or URI
A plain colon character is not legal in the path of a URL or URI. If you want to put a colon in the path, it must be percent-encoded. Furthermore, if this was a URL, it would start with a protocol; e.g. http:.
Now ... it is unclear what the HTTP stack you are using will do with a syntactically incorrect URL / URI, but it could simply be ignoring the colon and the characters after it.
Your code looks a bit odd too. You have tagged the question as [java]. But the code looks like JavaScript rather than Java; i.e. func is a Javascript keyword. But it also looks like you are using the (deprecated) com.sun.net.httpserver.HttpExchange Java class. I don't know what to make of that ...
My advice:
Don't use a colon character in the URL path.
If you must do it, then percent-encode the colon it.
If you cannot encode it properly, then you may need to find and use a different framework for your HTTP request handling. One that will accept and handle a malformed URL / URI in the way that you want. (Good luck finding one!)
Unfortunately, the details in your question are too sketchy to give more detailed advice.

Double asterisk in a request mapping

What does it mean when double asterisk is present in a request mapping?
For instance
#RequestMapping(value = { "/", "/welcome**" }, method =
RequestMethod.GET) public ModelAndView welcomePage() { ...
Universally speaking asterisks (in wildcard role) mean
/welcome* : anything in THIS folder or URL section, that starts with "/welcome" and ends before next "/" like /welcomePage.
/welcome** : any URL, that starts with "/welcome" including sub-folders and sub-sections of URL pattern like /welcome/section2/section3/ or /welcomePage/index.
/welcome/* : any file, folder or section inside welcome (before next "/") like /welcome/index.
/welcome/** : any files, folders, sections, sub-folders or sub-sections inside welcome.
In other words one asterisk * ends before next "/", two asterisks ** have no limits.
Ant paths
URL mapping ordering. From Spring Docs:
When a URL matches multiple patterns, a sort is used to find the most
specific match.
A pattern with a lower count of URI variables and wild cards is
considered more specific. For example /hotels/{hotel}/* has 1 URI
variable and 1 wild card and is considered more specific than
/hotels/{hotel}/** which as 1 URI variable and 2 wild cards
...
There are also some additional special rules:
The default mapping pattern /** is less specific than any other pattern. For example /api/{a}/{b}/{c} is more specific.
A prefix pattern such as /public/** is less specific than any other pattern that doesn’t contain double wildcards. For example
/public/path3/{a}/{b}/{c} is more specific.

Spring RestTemplate getForObject URL not working for Apple iTunes

I created the following simple test to query iTunes:
#Test
fun loadArtist()
{
val restTemplate = RestTemplate()
val builder = UriComponentsBuilder.fromHttpUrl("https://itunes.apple.com/search")
builder.queryParam("term", "howling wolf")
builder.queryParam("entity", "allArtist")
builder.queryParam("limit", 1)
println("\n\nURL ${builder.toUriString()}")
val result = restTemplate.getForObject(builder.toUriString(), String::class.java);
println("Got artist: $result")
}
And the output was unexpected:
URL https://itunes.apple.com/search?term=howling%20wolf&entity=allArtist&limit=1
Got artist:
{
"resultCount":0,
"results": []
}
Pasting the generated URL into a browser does give expected results - artist returned.
https://itunes.apple.com/search?term=howling%20wolf&entity=allArtist&limit=1
Also, hard-coding the query works:
val result = restTemplate.getForObject("https://itunes.apple.com/search?term=howling%20wolf&entity=allArtist&limit=1", String::class.java);
. . the problem only seems to occur for term queries that include spaces.
What went wrong? Other than assemble the URL by hand, how to fix?
Seems like a case of double encoding the whitespace. From the RestTemplate Javadoc:
For each HTTP method there are three variants: two accept a URI
template string and URI variables (array or map) while a third accepts
a URI. Note that for URI templates it is assumed encoding is
necessary, e.g. restTemplate.getForObject("http://example.com/hotel
list") becomes "http://example.com/hotel%20list". This also means if
the URI template or URI variables are already encoded, double encoding
will occur, e.g. http://example.com/hotel%20list becomes
http://example.com/hotel%2520list). To avoid that use a URI method
variant to provide (or re-use) a previously encoded URI. To prepare
such an URI with full control over encoding, consider using
UriComponentsBuilder.
So it looks like getForObject will actually query for https://itunes.apple.com/search?term=howling%2520wolf&entity=allArtist&limit=1 and thus result in an empty result. You can always just replace whitespaces with a "+" in your term or try to make one of those classes skip the encoding process.

Merging two relative file URLs

Let' say I have two paths, first can look like folder/ and second like /anotherFolder/image.png. I would like to merge those two paths in some automated fashion and with option for user to omit the last slash in first string and first slash in second string. So all of these
folder/ + /anotherFolder/image.png
folder + anotherFolder/image.png
folder + /anotherFolder/image.png
should give me folder/anotherFolder/image.png
I need to merge two properties in one of my projects and I want it as dummy as possible:)So is there some trick with URL class or do I have to play around with Strings?
You can do this with java.io.File, by using the constructor which takes a File and a String as arguments, will interpret the String as a relative path to the File.
Or with java.net.URL, you can send an URL and a String to the constructur, which will interpret the URL as a context for the String parameter.
I actually used FileUtils.getFile() from Apache Commons IO but Rolf's solution was working too.

Encode URL query parameters

How can I encode URL query parameter values? I need to replace spaces with %20, accents, non-ASCII characters etc.
I tried to use URLEncoder but it also encodes / character and if I give a string encoded with URLEncoder to the URL constructor I get a MalformedURLException (no protocol).
URLEncoder has a very misleading name. It is according to the Javadocs used encode form parameters using MIME type application/x-www-form-urlencoded.
With this said it can be used to encode e.g., query parameters. For instance if a parameter looks like &/?# its encoded equivalent can be used as:
String url = "http://host.com/?key=" + URLEncoder.encode("&/?#");
Unless you have those special needs the URL javadocs suggests using new URI(..).toURL which performs URI encoding according to RFC2396.
The recommended way to manage the encoding and decoding of URLs is to use URI
The following sample
new URI("http", "host.com", "/path/", "key=| ?/#ä", "fragment").toURL();
produces the result http://host.com/path/?key=%7C%20?/%23ä#fragment. Note how characters such as ?&/ are not encoded.
For further information, see the posts HTTP URL Address Encoding in Java or how to encode URL to avoid special characters in java.
EDIT
Since your input is a string URL, using one of the parameterized constructor of URI will not help you. Neither can you use new URI(strUrl) directly since it doesn't quote URL parameters.
So at this stage we must use a trick to get what you want:
public URL parseUrl(String s) throws Exception {
URL u = new URL(s);
return new URI(
u.getProtocol(),
u.getAuthority(),
u.getPath(),
u.getQuery(),
u.getRef()).
toURL();
}
Before you can use this routine you have to sanitize your string to ensure it represents an absolute URL. I see two approaches to this:
Guessing. Prepend http:// to the string unless it's already present.
Construct the URI from a context using new URL(URL context, String spec)
So what you're saying is that you want to encode part of your URL but not the whole thing. Sounds to me like you'll have to break it up into parts, pass the ones that you want encoded through the encoder, and re-assemble it to get your whole URL.

Categories