I have an API - http://localhost:8080/api/version/<versionA> where versionA is a Path Parameter.
HttpServletRequest getRequestURI() returns -> /api/version/<versionA>
How do we ensure that only /api/version is returned back? Is there any way to generalize this for all endpoints and return only the first part of the URI excluding path params?
For example -
/api/version/<param1> Should Return /api/version
/api/<param1> Should return /api
/api/version/<param1>/name/<param2> Should return /api/version
Path Parameter is not a Query Parameter; it is rather a part/segment of the URL Path. So, there is no (and there should not be) built-in Servlet API method, that will return your, "custom-sub-stringed" path.
Q: Why?
A: Because you may have more than one path parameters /a/b/<param1>/<param2>/c/<param3>
Is there any way to generalize this for all endpoints and return only the first part of the URI excluding path params?
In your case, one option would be to implement some utility method, which will find the the lastIndexOf "/" and then will return the substring to that last "/".
It depends a bit on how you've constructed things but let's take an example.
http://localhost:8080/sample/rest/v1/classLevel/methodLevel/param1/param2/something/param3
and a service defined as:
#Path("/v1/classLevel")
public class NewService {
#Path("/methodLevel/{param1}/{param2}/something/{param3}")
public Response getSomeStuff(#PathParam("param1") String param1,
#PathParam("param2") String param2,
#PathParam("param3") String param3,
#Context HttpServletRequest request) {
return Response.ok().build();
}
}
This webapp is deployed under the context root "sample". That means that if I were to go to http://localhost:8080/sample/ I'd get the root element (perhaps index.html) for the webapp. In my Application I have:
#ApplicationPath("/rest")
public class RestApplicationConfig extends Application {
// intentionally empty
}
So the parameters in the URL are:
request.getServletPath() returns "/rest"
request.getContextPath() returns "/sample"
request.getPathInfo() returns "/v1/classLevel/methodLevel/param1/param2/something/param3"
So I believe that what you want is request.getContextPath() + request.getServletPath(). But it's a bit unclear which part you really need.
EDIT
To find out what path is at the class level a little reflection is needed. Within a class that is being called (i.e. non-static methods of the NewService class above) you would be able to get that with:
public String getClassLevelPath() {
if( this.getClass().isAnnotationPresent(Path.class)) {
Path annotation = this.getClass().getAnnotation(Path.class);
return annotation.value();
}
return null;
}
As my class is defined, this returns "/v1/classLevel". I would personally cache this in something like a static variable as it's not going to change during runtime unless you're doing something else to change it.
Related
I'm trying to check if my method works through the API
#GetMapping(value = "/ads/in/rubrics/{ids}")
public List<Ad> findAllAdInRubricByIds(#PathVariable("ids") List<Integer> ids) {
return adService.findAllAdInRubricByIds(ids);
}
how can i set some parameters in get request?
that's how i tried
http://localhost:9999/mvc/ad/ads/in/rubrics/ids&ids=1&ids=2
http://localhost:9999/mvc/ad/ads/in/rubrics/ids&ids1=1&ids2=2
always get error 400 Bad Request
You're confusing PathVariables with RequestParams.
A PathVariable is a variable in the request path. It doesn't need to be the last character.
#GetMapping("/api/{version}/foo/{idFoo}")
public Void getFooNumber(#PathVariable("version") Integer version, #PathVariable("idFoo") Integer idFoo){
return "1";
}
Since PathVariables are part of the path, they're always required. If you don't incluide them in the request you'll end up invoking another endpoint or getting a 404 if the request can't be mapped to any endpoint.
The RequestParams are the parameters received at the end of the request URL, after the "?" character.
#GetMapping("/api/foo")
public Void getFooNumber(#RequestParam(value="version", required=false) Integer version, #RequestParam(value="idFoo", required=true) Integer idFoo){
return "1";
}
With RequestParams you can define for each one of them if it's required or not.
You can also mix them and have in the same method PathVariables and RequestParams.
In the first example the request URL would be ".../api/1/foo/25", while in the second example it would be ".../api/foo?version=1&idFoo=25"
As for having an array or a list, if you define the parameter as a List you can send multiple parameters of the same name:
#GetMapping("/ads/in/rubrics")
public Void findAllAdInRubricByIds(#RequestParam(value="ids", required=true) List<Integer> ids){
return adService.findAllAdInRubricByIds(ids);
}
In this case, you can use ".../ads/in/rubrics?ids=1&ids=2&ids=3&ids=4"
http://localhost:9999/mvc/ad/ads/in/rubrics/?ids1=1&ids2=2
For the first parameter use a ? and after that for each additional parameter a &
We are using RESTful Web Services (Jersey) for API calls on java. While API needs optional parameter, we are doing as:
api-interface/user/userid/9000/companyid/90909/{optionalparameter*}
and we have to call this api when there is no optional parameter as:
api-interface/user/userid/9000/companyid/90909/
What needed is:
Case:1 If optional parameter exists
api-interface/user/userid/9000/companyid/90909/name/john/address/MA/age/34
Case:2 If Optional parameter doesn't exists.
api-interface/user/userid/9000/companyid/90909
My present implementation is:
#GET
#Path("user/companyid/{companyid}/userid/{userid}/{optionalparameter:.*}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUserList(#PathParam("companyid") String companyId, #PathParam("userid") String userId,
#PathParam("optionalparameter") String syncDate) throws BadRequestException, InternalServerException {
//parsing the param.
}
In above code, I need to add trailing "/" but I am looking the way to remove this trailing "/" if anybody doesn't wants to pass those param.
I followed this link but it didn't worked while the preceding parameter's length is more then 1.
Please, Suggest me the best way.
Looking at your reference, have you tried this:
#Path("userid/{userid}/companyid/{companyid}{optparam:(/[^/]+?)*}")
public Response getLocation(
#PathParam("userid") int userid,
#PathParam("companyid") int companyid,
#PathParam("optparam") String optparam) {
String[] params = parseParams(optparam);
...
}
private String[] parseParams(String params) {
if (params.startsWith("/")) {
params = path.substring(1);
}
return params.split("/");
}
That should work, giving you all the parameters in a single array.
EDIT: I have updated the search string and verified it on a local install.
I'm trying to create an API using JAX-RS with multiple ways to get products. So if you type for example
herp.com/products/11
You get the product with ID 11, this works perfectly. And if you type:
herp.com/products/name/banana
You should get all products called banana. Although the get by name functionality doesn't work (the in-parameter name is always null). Here's the function with all annotations.
#GET
#Path("/name/{name}")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getByName(#PathParam("{name}") String name) {
Logger.getAnonymousLogger().log(Level.INFO, "getByName, name: " + name);
List<ProductProxy> found = new ArrayList<ProductProxy>();
for(ProductProxy proxy : products)
if(name.equals(proxy.getName()))
found.add(proxy);
return Response.ok(found).build();
}
Although the in-paremeter always is null. Am i doing something wrong with the annotations?? The class anotation is #Path("products").
Remove the curly braces from your path param, i.e. change
#PathParam("{name}") String name
to
#PathParam("name") String name
The braces in the #Path("foo/{bar}) just identify that you have a path parameter named bar, but aren't a part of the parameter's name.
I've a nicely working Spring argument mapping for my arguments, unfortunately I've one component which isn't passed through the DispatcherServlet and I struggle to find a way to get the mapping to run somehow manually.
An example would look like this:
public class Bar {
public Bar(#RequestParam("map") String map) {
System.out.println("Found: " + map); // Expected result "Found: value
}
}
public class Foo {
public static void main(String[] args) {
context = new ClassPathXmlApplicationContext("META-INF/context.xml");
String pseudoUrl = "/?map=value"
Bar bar = context.getBeans('bar', pseudoUrl);
}
}
It that something which could work somehow? As you can imaging the passed around data has a more complex structure and therefore I'd like to avoid that I've to write an parser on my own.
Cheers.
You could basically follow the same algorithm that DispatcherServlet.doDispatch() does:
for each HandlerMapping in the context, (line 1058 of DispatcherServlet in spring-webmvc.jar 3.1.2-RELEASE), test if HandlerMapping.getHandler(request) returns non-null
for each HandlerAdapter in the context, (line 1091) test if HandlerAdapter.supports(handlerMapping) returns true
execute HandlerAdapter.handle(request, ...) and handle the output (line 923)
This would require you to bundle up your pseudoUrl into a pseudo-HttpServletRequest, etc.
On the other hand, if you know that you always want to get a specific bean from the context in your main() method, I would just ask Spring for that bean and invoke the method explicitly, rather than dealing with mapping beans to a URL (which I assume does not change).
Background: we have build a RESTful API using Jersey a while ago where we map the uri /items.json to a json array of ids and /items/{id}.json to the json object of a single item. Now we want to create a list with some meta data for each item and would like to use a selector like /items.data.json, similar to apache sling.
So far: I just extended the UriConnegFilter to parse the uri for additional suffixes, something like this:
public class UriSelectorFilter extends UriConnegFilter {
protected List<String> selectors; // this list is populated in the constructor
public ContainerRequest filter(ContainerRequest request) {
super.filter(request);
// search for suffix in last path segment, see http://java.net/projects/jersey/sources/svn/content/trunk/jersey/jersey-server/src/main/java/com/sun/jersey/api/container/filter/UriConnegFilter.java?rev=5034
final String[] suffixes = segment.getPath().split("\\.");
for (int i = suffixes.length - 1; i >= 1; i--) {
final String suffix = suffixes[i];
if(selectors.contains(suffix)) {
request.getQueryParameters().putSingle("selector", suffix);
final int index = path.lastIndexOf('.' + suffix);
path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();
suffixes[i] = "";
}
}
if (length != path.length()) {
request.setUris(
request.getBaseUri(),
request.getRequestUriBuilder().replacePath(path).build());
}
return request;
}
}
This filter works perfect, it finds the selector part of my uri and adds a query param to the request object. But in my Resource I added a #QueryParam attribute, which is only filled with the default value and not the added query value:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getItemsJSON(#DefaultValue("id") #QueryParam("selector") String selector) {
// query param is not filled with the selector that was found in the UriSelectorFilter
}
Does anybody have a suggestion how I can provide my resource with the selector that was detected? Is there a better way than using a QueryParam? (Note: if I add the query to my url like '?selector=something' then the attribute is filled correctly.)
Any help is very appreciated.
You need another argument that is annotated with #PathParam, and you need to specify in your #Path annotation (on the method or class) how to bind these bits together. For example, to deal with a path like /items/foobar42/data.json you might do this:
#GET
#Path("/items/{item}/data.json")
#Produces(MediaType.APPLICATION_JSON)
public Response getItemsJSON(#PathParam("item") String itemId,
#DefaultValue("id") #QueryParam("selector") String selector) {
// Now you've got an itemId and a possible selector...
}
Trying to do all the mapping with a filter… that seems difficult to me given that there's a nice declarative way of doing it instead. You can even specify a regular expression in the #Path to allow for matching a more complex variable section; I do that in my own code to create a method that can serve a whole hierarchical filesystem.
(Note that the {braced} item in the #Path should match the name in the #PathParam annotation, and you can have multiple items matched from the path if necessary; just use several #PathParam-annotated arguments.)