Ignoring "/" inside a URL path variable value - spring

I have a very odd situation. I have an API endpoint which has to accept requests to list all the resources available for the given endpoint and list a specific resource. The endpoint looks like this :
/v1/patients
I can call this without passing any path variables and it will list all patient records. Here comes the special case - If I want to fetch only one patient, I have to pass in the Patient number as path variable - and My patient number looks something like this - 2019/patientname/October/27 - and that is the problem.
when I pass the above number as path variable, I will not get the resource I wanted, instead Spring considers the slashes "/" as separators.
Example :
/v1/patients/2019/patientname/October/27
And I am getting the response like given path is not found - of course there is no path like that.
Is there a possibility of Ignoring all the slashes in between this one Patient number?
Edit : There is no possibility of changing the Patient number as it is part of a very old and legacy code-base, on which the whole system is running.
Edit 2 - URL encoding cannot be used in this situation - this patient number has to be passed in as it is - Situation is very insane.

You could try something like:
#GetMapping("/v1/patients/{year}/{name}/{month}/{day}")
public ResponseEntity<Patient> getPatient(#PathVariable Integer year,
#PathVariable String name,
#PathVariable String month,
#PathVariable Integer day) {
String patientNumber = year + "/" + name + "/" + month + "/" + day;
...
}
And here's something that may also work:
#GetMapping("/v1/patients/**")
public ResponseEntity<Patient> getPatient(HttpServletRequest request) {
String patientNumner = (String)
request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
...
}

Related

Can we have two #PathVariable. One as the actual Path Variable and other for swagger to document it as Deprecated?

I am trying to change the REST call's #PathVariable. The existing Path Variable is formed by combination of three parameters. The change is to handle Path Variable formed by combination of two parameters. I need this change to be documented in swagger with the earlier shown as Deprecated.
I have tried to use both Path Variable with one as #Deprecated like below
#Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", required=false )
#Deprecated #PathVariable String x_y_z,
#Parameter(description = "x_y - x is first ID, y is second ID", required=true )
#PathVariable String x_y)
The have changed request mapping value from /aaa/bbb/{x_y_z} to below
#RequestMapping(value = "/aaa/bbb/{x_y}", method = RequestMethod.GET, produces = "application/json"
With above changes the request fails with 500 error, may be since it is expecting two Path Variables. But the swagger documentation is as expected.
I tried to remove #PathVariable for x_y_z. The request is processed as expected and the swagger now shows x_y_z as deprecated but shows the parameter as (query) instead of (path)
Any suggestions please
Assuming an #RestController and that Swagger understands #Deprecated for a method:
#Deprecated
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}_{z:\\d+}")
public ResponseEntity<MessageResponse> getStuff(#PathVariable String x,
#PathVariable String y,
#PathVariable(name = "z", required = false) String z) {
return getNewStuff(x, y); //send to the other method and ignore z
}
#GetMapping("/aaa/bbb/{x:\\d+}_{y:\\d+}")
public ResponseEntity<MessageResponse> getNewStuff(#PathVariable String x,
#PathVariable String y) {
// do stuff for x and y by default
return ResponseEntity.ok(new MessageResponse("this method is supported for " + x + " and " + y));
}
The RegEx should look for digits as the path variables, interspersed with underscores.
NB: leaving this part of the answer if Swagger works with it instead with the understanding that it could be deprecated:
#PathVariable #Parameter(description = "x_y_z - x is first ID, y is second ID, z is third ID", deprecated = true) String z
Deprecating the original method and introducing a new method with the correct parameters but different RequestMapping could also be a valid workaround.
The other part to note is that it is more common to use slashes as the delimiter rather than underscores in Spring (e.g., /aaa/bbb/x/y). You also may wish to include a validator that fits your requirements.

URI within endpoint

I have a URI string inside the request that I am supposed to make. How to extract it and write a proper controller.
markerURI = marker://markerType/markerValue
Request:
POST /books/123/markers/marker://big/yellow
I have written below rest controller for the above request:
#PostMapping("/books/{id}/markers/{markerURI:^marker.*}")
public void assignMarker(
#PathVariable("id") String id,
#PathVariable("markerURI") String markerURI
)
but i'm not able to get markerURI=marker://big/yellow inside markerURI variable. The request show 404 Not found error. Is there any way to do this. It's a requirement so can't do any hacks.
Edit:
markerURI can contain attributes like marker://markerType/markerValue?attr1=val1&attr2=val2
As per https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestmapping-uri-templates
You can have your url pattern in below pattern
"/resources/ima?e.png" - match one character in a path segment
"/resources/*.png" - match zero or more characters in a path segment
"/resources/**" - match multiple path segments
"/projects/{project}/versions" - match a path segment and capture it as a variable
"/projects/{project:[a-z]+}/versions" - match and capture a variable with a regex
but your url pattern is defined as a url inside a url, for that I suggest you to use below method and concatenate your result after fetching the values from uri as pathvariable.
#PostMapping("/books/{id}/markers/{marker:[a-z]+}://{markerType:[a-z]+}/{markerValue:[a-z]+}")
public void assignMarker(#PathVariable("id") String id,#PathVariable("marker") String marker,
#PathVariable("markerType") String markerType,
#PathVariable("markerValue") String markerValue) {
String markerUri = "/"+marker+"://"+markerType+"/"+markerValue;
System.out.println(markerUri);
}

How to make Get Request with Request param in Postman

I have created an endpoint that accepts a string in its request param
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression") String expression) {
System.out.println(expression);
// code to validate the input string
}
While sending the request from postman as
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606/Curr_month:Y07608
// lets say this is a valid input
console displays as
Y07607=Curr_month:Y07606/Curr_month:Y07608 Valid
But when i send
https://localhost:8443/validate?expression=Y07607=Curr_month:Y07606+Curr_month:Y07608
//which is also an valid input
console displays as
Y07607=Curr_month:Y07606 Curr_month:Y07608 Invalid
I am not understanding why "+" is not accepted as parameter.
"+" just vanishes till it reaches the api! Why?
I suggest to add this regular expression to your code to handle '+' char :
#GetMapping(value = "/validate")
private void validateExpression(#RequestParam(value = "expression:.+") String expression) {
System.out.println(expression);
// code to validate the input string
}
I didn't find any solution but the reason is because + is a special character in a URL escape for spaces. Thats why it is replacing + with a " " i.e. a space.
So apparently I have to encode it from my front-end
Its wise to encode special characters in a URL. Characters like \ or :, etc.
For + the format or value is %2. You can read more about URL encoding here. This is actually the preferred method because these special characters can sometimes cause unintended events to occur, like / or = which can mean something else in the URL.
And you need not worry about manually decoding it in the backend or server because it is automatically decoded, in most cases and frameworks. In your case, I assume you are using Spring Boot, so you don't need to worry about decoding.

Jackrabbit XPath Query: UUID with leading number in path

I have what I think is an interesting problem executing queries in Jackrabbit when a node in the query path is a UUID that start with a number.
For example, this query work fine as the second node starts with a letter, 'f':
/*/JCP/feeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
This query however does not, if the first 'f' is replaced with '2':
/*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
The exception:
Encountered "-" at line 1, column 26.
Was expecting one of:
<IntegerLiteral> ...
<DecimalLiteral> ...
<DoubleLiteral> ...
<StringLiteral> ...
... rest omitted for brevity ...
for statement: for $v in /*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence] return $v
My code in general
def queryString = queryFor path
def queryManager = session.workspace.queryManager
def query = queryManager.createQuery queryString, Query.XPATH // fails here
query.execute().nodes
I'm aware my query, with the leading asterisk, may not be the best, but I'm just starting out with querying in general. Maybe using another language other than XPATH might work.
I tried the advice in this post, adding a save before creating the query, but no luck
Jackrabbit Running Queries against UUID
Thanks in advance for any input!
A solution that worked was to try and properly escape parts of the query path, namely the individual steps used to build up the path into the repository. The exception message was somewhat misleading, at least to me, as in made me think that the hyphens were part of the root cause. The root problem was that the leading number in the node name created an illegal XPATH query as suggested above.
A solution in this case is to encode the individual steps into the path and build the rest of the query. Resulting in the leading number only being escaped:
/*/JCP/_x0032_eeadeaf-1dae-427f-bf4e-842b07965a93//*[#sequence]
Code that represents a list of steps or a path into the Jackrabbit repository:
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.util.ISO9075;
class Path {
List<String> steps; //...
public String asQuery() {
return steps.size() > 0 ? "/*" + asPathString(encodedSteps()) + "//*" : "//*";
}
private String asPathString(List<String> steps) {
return '/' + StringUtils.join(steps, '/');
}
private List<String> encodedSteps() {
List<String> encodedSteps = new ArrayList<>();
for (String step : steps) {
encodedSteps.add(ISO9075.encode(step));
}
return encodedSteps;
}
}
Some more notes:
If we escape more of the query string as in:
/_x002a_/JCP/_x0032_eeadeaf-1dae-427f-bf4e-842b07965a93//_x002a_[#sequence]
Or the original path encoded as a whole as in:
_x002f_a_x002f_fffe4dcf0-360c-11e4-ad80-14feb59d0ab5_x002f_2cbae0dc-35e2-11e4-b5d6-14feb59d0ab5_x002f_c
The queries do not produce the wanted results.
Thanks to #matthias_h and #LarsH
An XML element name cannot start with a digit. See the XML spec's rules for STag, Name, and NameStartChar. Therefore, the "XPath expression"
/*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
is illegal, because the name test 2eead... isn't a legal XML name.
As such, you can't just use any old UUID as an XML element name nor as a name test in XPath. However if you put a legal NameStartChar on the front (such as _), you can probably use any UUID.
I'm not clear on whether you think you already have XML data with an element named <2eead...> (and are trying to query that element's descendants); if so, whatever tool produced it is broken, as it emits illegal XML. On the other hand if the <2eead...> is something that you yourself are creating, then presumably you have the option of modifying the element name to be a legal XML name.

Passing date parameters to #url.action via ajax

In my ASP.NET MVC 4 app, I'm using the following JAX code taken from this StackOverflow post to pass Date parameters to a controller but I am getting the following http 404 error: "The resource you are looking for has been removed, had its name changed, or is temporarily unavailable. Requested URL /myWebApp/myController/myAction/01/01/2014/12/31/2014"
Here the input controls txtFrom and txtTo have the values 01/01/2014 and 12/31/2014 respectively. the issue is that MVC is probably interpreting each date as three different parameters. How can we fix it. I tried replacing $('#txtFrom').val() with $('#txtFrom').val().replace("///g", "_") but it does not work.
window.location.href = '#Url.Action("myAction")/' + $('#txtFrom').val() + '/' + $('#txtTo').val();
Action method:
public ActionResult myAction(string startDate, string endDate)
{
//simple code here to use the input parameters
}
You could either format the date string with Razor
#HttpUtility.UrlEncode(date)
with javascript
encodeURIComponent(date)
or pass the date as ticks (milliseconds since Epoch) instead of the human-readable format.
Edit:
After experimenting with this and a bit of research it seems the slash and %2f encoding causes all kinds of problems. Stick to the millisecond representation for a date and not worry about passing the slash.
window.location.href is not ajax. Its your browser making a HTTP get request to the url. In your case, its not a complete url, but a partial; thus the error. You may try the following for start. Substitute the hardcoded values for dates with your inputs
$.getJSON({‘#Url.Action("myAction")’ + '/', { startDate: ‘1/1/2001’, endData: ‘1/2/2002’ }});
If you want to process any return value; refer to jquery documentation on $.getJSON (http://api.jquery.com/jquery.getjson/)

Resources