I am working to cater REST url of three types:
url/detail/3 (integer only)
url/detail/hello (String only)
url/detail/3/1d
For URL 1 and 3 I am using method1 and for URL 2 method2 is used.
Problem 1: All requests type of 1 and 2 matches method 2 only.Though I've specified Integer pattern in method 1 for queries having integer specifically.
Problem 2: To use an optional param (like in 3) I am using method1 because jersey doesn't provide any option for optional param.But url types of url/detail/3/1d is never matched as specified in method 1.
Please help me understand what I am doing wrong as I am newbie to jersey.
#GET
#Path("/detail/{id: \\d+}/{time-period:(/time-period/[^/]+?)?}")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes(MediaType.APPLICATION_JSON)
public JResponse method1(
#Context HttpHeaders headers,
#PathParam("id") String id,
#PathParam("time-period") String timePeriod) {
if(timePeriod == null || timePeriod.equals(""))
{
//code
}
else
//code
}
#GET
#Path("/detail/{name}")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes(MediaType.APPLICATION_JSON)
public JResponse method2(
#Context HttpHeaders headers, #PathParam("name") String name) {
//code
}
Maybe a missing whitespace leads to the problem. See Optional #PathParam in Jax-RS
You can define default values for parameters with #DefaultValue("1000")
Instead of complicated regexps you should probably use subresource.
#Path("detail/{id}{time-perioid:(/[^/]+?)?}")
http://x.y.z:4080/analytics/internal/detail/kala
2014-01-17 07:35:50,509 [http-nio-4080-exec-8] INFO xxx - id: kala
2014-01-17 07:35:50,510 [http-nio-4080-exec-8] INFO xxx - time-period:
and
http://x.y.z:4080/analytics/internal/detail/kala/123
2014-01-17 07:36:01,644 [http-nio-4080-exec-9] INFO xxx - id: kala
2014-01-17 07:36:01,645 [http-nio-4080-exec-9] INFO xxx - time-period: /123
If id can be string or integer I would go validating it inside handler.
Related
In Spring Boot 1.5.4 I have a request mapping like this:
#RequestMapping(value = "/graph/{graphId}/details/{iri:.+}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public JSONObject getGraph(#PathVariable Long graphId,
#PathVariable String iri) {
log.debug("Details called for graph ID {} for IRI {}", graphId, iri);
return detailsService.getDetails(graphId, iri);
}
Accessing
http://localhost:9000/api/v1/graph/2/details/http%3Anthnth33
works fine and the server maps the request correctly and the code returns the expected result
But accessing
http://localhost:9000/api/v1/graph/2/details/http%3A%2F%2Fserverurl.net%2Fv1%2Fus%2Fh.schumacher%408tsch.net%2Fn%2FLouSchumacher
gives a bad server request (Failed to load resource: the server responded with a status of 400 (Bad Request)). The request mapping to the end point isn't even done in that case.
Obviously the slash '/' encoded as %2F (using encodeURIComponent()) causes trouble. Why? What am I missing? How should uri parameter then be encoded?
The question is not only about how to extract PathVariables but more on how to force String to recognize the correct mapping.
The issue with your example is how Spring is doing path matching. The URL you have provided as example
http://localhost:9000/api/v1/graph/2/details/http%3A%2F%2Fserverurl.net%2Fv1%2Fus%2Fh.schumacher%408tsch.net%2Fn%2FLouSchumacher
will be decoded into by container
http://localhost:9000/api/v1/graph/2/details/http://serverurl.net/v1/us/h.schumacher#8tsch.net/n/LouSchumacher
before processing by Spring matcher. This makes matche think that this only http: corresponds {iri:.+} and as later goes / so it is some longer path you don't have a mapping for.
The approach described here should work for you: Spring 3 RequestMapping: Get path value
#RequestMapping(value = "/graph/{graphId}/details/**",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public JSONObject getGraph(#PathVariable Long graphId,
HttpServletRequest request) {
String iri = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
log.debug("Details called for graph ID {} for IRI {}", graphId, iri);
return detailsService.getDetails(graphId, iri);
}
So I have in my code POST method :
#POST
#Path("/send/{userPost}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces("application/json")
public Response sendUser(#PathParam("userPost") String userPost ) {
List<Post>userPosts = new ArrayList();
Post post = new Post(99,userPost,"Bartek Szlapa");
userPosts.add(post);
User user = new User(99,"Bartek","Szlapa",userPosts);
String output = user.toString();
return Response.status(200).entity(output).build();
}
unfortunately its not working. I'm getting 404 error. Server is configured correctly because other methods work perfectly. Funny thing is that when I remove {userPost} , parameter : #PathParam("userPost") String userPost and send empty request : http://localhost:8080/JavaAPI/rest/api/send it works - I'm getting new User object with null at some fields. Do you know why I cannot send parameter ? Thanks in advance for help! :)
What you are sending is not a path parameter to send your value as a path parameter based on your api , let us say you are trying to send "test"
http://localhost:8080/JavaAPI/rest/api/send/test
if you want to use query params
#POST
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
#Produces("application/json")
public Response sendUser(#QueryParam("userPost") String userPost ) {
and your request should be
http://localhost:8080/JavaAPI/rest/api/send?userPost=test
Your "userPost" parameter is not in the Path : localhost:8080/JavaAPI/rest/api/send?=test
You defined this path :
#Path("/send/{userPost}")
So, your URI should be :
localhost:8080/JavaAPI/rest/api/send/test
I want to design an endpoint similar to
$host/api/products?price=under+5
How can I use '+' in queryparam?
I could do like this to get that url
#GET
#Path("/products?price=under+{price}")
But How can I do using #QueryParam? If I use the following,
#GET
#Path("/products")
#UnitOfWork
public Response getProducts(#NotNull #QueryParam("price") String price) {
I get
$host/api/products?price=5
The value of the price query parameter must be URL encoded. When URL encoded, the + character becomes %2B. So you'll have under%2B5.
With it, the following should work fine:
#GET
#Path("/products")
public Response getProducts(#NotNull #QueryParam("price") String price) {
// the value of price will be: under+5
...
}
If you don't want the JAX-RS runtime to decode the price parameter, annotate it with #Encoded.
So I have to create a filter that adds/formats date strings.
My implementation was to create a ContainerRequestFilter and perform formatting there; then add this custom filter through #NameBinding.
My problem seems to be that #NameBinding is ignored when used with #PreMatching, thus not being able to work since I also use reflection to extract properties from my filter/annotation.
So after performing formatting in filter, the idea is to use:
uriBuilder.replaceQueryParam(startDateQueryParamName, formattedString);
but even if I add a hardcoded value, the value is still the original.
Say I make a request: .../api/x?startDate=1234-01-01T00:00:00
And I hardcode in filter:
`uriBuilder.replaceQueryParam(startDateQueryParamName, "2020-05-05T00:00:00");`
I still get 1234-01-01T00:00:00 in resource method:
#GET
#Path("/t1")
#Produces(MediaType.TEXT_PLAIN)
#StartEndDateFilter(required = true)
public String testLocalDateTime(#QueryParam("startDate") LocalDateTime startDate, #QueryParam("endDate") LocalDateTime endDate, #Context UriInfo urinfo) {
MultivaluedMap<String, String> m = urinfo.getQueryParameters();
String d = startDate == null ? "nothin " : startDate.toString();
String e = endDate == null ? "nothin " : endDate.toString();
return String.format("start: %s \nend: %s", d, e);
}
So, I thought maybe using #PreMatching would help but, as I mentioned, this shows a warning:
Warning: #PreMatching provider, class com.api.DateRangeFilter, also
annotated with a name binding annotation. Name binding will be
ignored.
And on top of that, when I call requestContext.setRequestUri(uriBuilder.build()); I get the following error when I call the endpoint:
Warning: StandardWrapperValve[com.api.Ap]: Servlet.service() for
servlet com.api.Ap threw exception java.lang.IllegalStateException:
Method could be called only in pre-matching request filter. at
org.glassfish.jersey.server.ContainerRequest.setRequestUri(ContainerRequest.java:411)
at com.api.DateRangeFilter.filter(DateRangeFilter.java:153)
line 153 is:
requestContext.setRequestUri(uriBuilder.build());
I have a Spring controller with two parameter long and String:
#RequestMapping(value = "/webpage")
#Controller
public class WebpageContentController {
//...
#RequestMapping(value = "{webpageId}/{webpageAddress}", method = RequestMethod.GET)
public String contentWebpageById(#PathVariable long webpageId, #PathVariable String webpageAddress) {
System.out.println("webpageId=" + webpageId);
System.out.println("webpageAddress=" + webpageAddress);
//...
}
//...
If I invoke it like this:
http://localhost:8080/webarch/webpage/1/blahblah
All is fine:
webpageId=1
webpageAddress=blahblah
But If I pass String parameter with slash (in this case URL address):
http://localhost:8080/webarch/webpage/1/https://en.wikipedia.org/wiki/Main_Page
I get an error:
org.springframework.web.servlet.PageNotFound.noHandlerFound No mapping found for HTTP request with URI [/webarch/webpage/1/https://en.wikipedia.org/wiki/Main_Page] in DispatcherServlet with name 'appServlet'
How pass such parameter?
Well the error is caused by springs controllers mapping, when Spring sees url like
http://localhost:8080/webarch/webpage/1/https://en.wikipedia.org/wiki/Main_Page
It doesn't 'know' that the 'https://en.wikipedia.org/wiki/Main_Page' should be mapped as parameter to "{webpageId}/{webpageAddress}" mapping since every slash is interpreted as a deeper controler method mapping. It looks for controller method mapping like (webpage/1/http:{anotherMapping}/wiki{anotherMapping}/Main_Page{anotherMapping}) wich this kind of mapping is obviously not handled by "{webpageId}/{webpageAddress}"
EDIT
According to your comment you can try something like this
#RequestMapping(value = "/{webpageId}/**", method = RequestMethod.GET)
public String contentWebpageById(HttpServletRequest request) {
String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
String extractedPathParam = pathMatcher.extractPathWithinPattern(pattern, request.getServletPath());
extractedPathParam = extractedPathParam.replace("http:/", "http://");
extractedPathParam = extractedPathParam.replace("https:/", "https://");
//do whatever you want with parsed string..
}
Using spring 4.2.1
SomeParsing should use some Regular Expression to extract only the URL 'variable'
Just encode all special characters in the URL.
https://en.wikipedia.org/wiki/Main_Page
becomes this:
https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMain_Page
and you can pass it as URL parameter without any problems. Decoding is done automatically, so if you access the parameter as variable in your controller, it contains the URL already decoded and you can use it without any converting needed.
More information about URL encoding: https://en.wikipedia.org/wiki/Percent-encoding