I am using Kathatrsis for my REST API. I am also new to the JSONApi spec. I for the life of me cant figure out the url pattern to call the #JsonApiFindAllWithIds method.
For example, say it is annotated as:
#JsonApiFindAllWithIds
public Iterable<ThriftType> findAll(Iterable<String> iterable, QueryParams queryParams) {
And if I call the URL (my resource is called test):
http://localhost:8080/test/1?filter[test][otherId][EQ]=9
I would expect it to hit that method, with an iterable list containing 1 and a query param object containing my filter.
However, it calls my #JsonApiFindOne method described as:
#JsonApiFindOne
public ThriftType findOne(String id) {
Can you tell me the proper url format to hit my #JsonApiFindAllWithIds method?
JsonAliFindAllWithIds is for scenarios where there are more than one id for which value needs to be fetched.
For eg
http://localhost:8080/test/1,2
Would call findallwithids method whereas providing a single
Id would call findone method.
Hope it helps
Related
I'm developing API using Spring Framework and faced a problem that can be solved by simply adding a necessary logic to every place I have it, but I think that there might be an elegant solution to fix it.
I have the following method in my controller:
#GetMapping("/user/{userId}/permissions")
public List<PermissionDto> list(#PathVariable long userId,
#ModelAttribute #Valid PermissionCriteria criteria) {
return permissionService.list(criteria);
}
The thing is that in dto I have a field called userId. It's made not to have a lot of arguments going to the method of the service. But, I want this user id to be set exactly from path since I use the URL that specifies that we are adding permission exactly to specific user resource. It's doable by making addition line that uses setter in the criteria and sets the value of userId. However, now I should never forget to add this line every time I have a case like that. That's why I decided to move it to InitBinder:
#InitBinder(PERMISSIONS_CRITERIA_NAME)
public void permissionsCriteriaInitBinder(WebDataBinder binder) {
PermissionsCriteria criteria = (PermissionsCriteria) binder.getTarget();
Optional.ofNullable(requestHelper.getUserId())
.map(Long::parseLong)
.ifPresent(criteria::setUserId);
}
It works fine. The user ID is set from the path. However, If I specify request parameter and path variable at the same time, even though userId is set from the path in init binder, it's overridden afterwards before it goes to the controller method. So, this one doesn't solve all the issues.
What I want to find, is someplace where the logic can be put to apply to both init binder(I need it for validation) and controller method. Maybe there is a special type of hook or interceptor or at least something to implement to satisfy this conditions?
I have to following endpoint structure in Jersey:
/objects/
/objects/:id
/objects/:id/:field
/objects/:id/:field/:subfield
The IDs I'm using have a very specific format, so I first check if the format is valid before making a request to the database.
Right now I have to put this code in each of the POST, PUT, GET, DELETE functions for each of the functions that has :id as a parameter. So this just means an early return.
if (!isIdValid(id)){
return Response.status(Response.StatusType.BAD_REQUEST)
.entity("The ID you've provided is invalid")
.build();
}
(In reality the error entity is an object containing more information about the error)
And then for each function using the :field or :subfield parameters the code is similar. This checking and error-handling behavior has to be copied every time. And when I start copy-pasting stuff, I start thinking: there should be a better way?
I would like to place the :id checking code at the the /objects/:id level, and then all further nested levels are assumed have a valid ID. The same for the other parameters further nesting down.
I've been looking into using subresource locators, but then you create a function returning a new instance of the subresource. I can't put a conditional return of a Response-object at that level for if the validation fails.
#Path("{id}")
function Class<ObjectFieldResource> getObjectById(#PathParam("id") String id){
return ObjectFieldResource.class;
}
I could start throwing exceptions, but I would rather avoid that, since I don't really consider invalid input to be an exception.
How would such a structure best be implemented? I've looked at bean validation but that doesn't seem to allow me to define validation for my specific format + custom error responses.
Am I missing something in the way subresources should be implemented?
Solution 1
If you can use regexp checks instead of your isIdValid method it's possible to define your resources like this
#POST
#Path("objects/{id:\\d+}")
public Response doSmth(#PathParam("id") String id) {
...
}
In a case of invalid id format caller will have 'Not Found' response status without even reaching your doSmth method.
Obviously, you can use String constants for all equal path values.
final static String ID_RES = "objects/{id:\\d+}";
#POST
#Path(ID_RES)
public Response postSmth(#PathParam("id") String id) {
...
}
...
#GET
#Path(ID_RES)
public Object getSmth(#PathParam("id") String id) {
...
}
The can also read full description of Path#value parameter
Solution 2
Create and register at your REST server javax.ws.rs.container.ContainerRequestFilter implementation with filter method having needed URI checks.
The single filter parameter has ContainerRequestContext type from witch you can call getUriInfo for getting URI and method abortWith(Response response) which can be used for aborting caller request if your resource ids validation was failed.
See Chapter 10. Filters and Interceptors chapter of Jersey Manual.
I'm building an Android App and am using Square's Retrofit library for short-lived network calls. I'm relatively new to Java and Android. Until now I've constructed requests like so:
#GET("/library.php")
void library(
#Query("one_thing") String oneThing,
#Query("another_thing") String anotherThing,
Callback<Map<String,Object>> callback
);
And called them like so:
service.library(oneThing, anotherThing, callback);
I need to implement a request that accepts a variable number of parameters, not more than 10 or so. It's cumbersome to have to define them individually and pass null or something for the ones that aren't present for a given request. Is there a way to define an interface for a request such that it accepts a variable number or parameters and auto-constructs #Querys for each element in the parameter dictionary/map? Something like this:
#GET("/library.php")
void library(
Map<String,Object> parameters,
Callback<Map<String,Object>> callback
);
service.library(parameters, callback);
Thanks in advance for any tips.
Edit: passing null for params that aren't pertinent to the request wont work in this case. Ideally I'd be able to set/create #Querys based on the parameter dictionary, so that keys wont become a #Query if their value is null.
Edit: I'm specifically looking for a solution that works with GET requests.
You could always try passing the parameters as a HTTP Body instead, such as in this example (note: I'm the author)
But as you suggest, use a Map with your values instead, so this might work for you:
#POST("/library.php")
public void library(#Body Map<String, Object> parameters, Callback<Map<String,Object>> callback);
It's a bit late but I'll post it anyway just in case someone found the same problem on Google.
They've introduced the Annotation #QueryMap
This annotation let you pass and object that implements Map class, is not as nice as Post requests that let's you pass an Object as parameter but it get the works done.
#GET("/library.php")
public void library(#QueryMap Map<String, Object> parameters, Callback<Map<String,Object>> callback);
This question already has answers here:
JAX-RS: Multiple paths
(4 answers)
Closed 2 years ago.
Can we have more than one #Path annotation for same REST method i.e. the method executed is the same, but it is executed on accessing more than one URL?
E.g.: I want to run the searchNames() method on both http://a/b/c and http://a/b.
You can't have mutliple #Path annotations on a single method. It causes a "duplicate annotation" syntax error.
However, there's a number of ways you can effectively map two paths to a method.
Regular expressions in #Path annotation
The #Path annotation in JAX-RS accepts parameters, whose values can be restricted using regular expressions.
This annotation:
#Path("a/{parameter: path1|path2}")
would enable the method to be reached by requests for both /a/path1 and /a/path2. If you need to work with subpaths, escape slashes: {a:path1\\/subPath1|path2\\/subPath2}
Serving responses with a redirection status code
Alternatively, you could set up a redirection. Here's a way to do it in Jersey (the reference implementation of JAX-RS), by defining another subresource. This is just an example, if you prefer a different way of handling redirections, feel free to use it.
#Path("basepath")
public class YourBaseResource {
//this gets injected after the class is instantiated by Jersey
#Context
UriInfo uriInfo;
#Path("a/b")
#GET
public Responce method1(){
return Response.ok("blah blah").build();
}
#Path("a/b/c")
#GET
public Response method2(){
UriBuilder addressBuilder = uriInfo.getBaseUriBuilder();
addressBuilder.path("a/b");
return Response.seeOther(addressBuilder.build()).build();
}
}
Using a servlet filter to rewrite URLs
If you're going to need such functionality often, I suggest intercepting the incoming requests using a servlet filter and rewriting the paths on the fly. This should help you keep all redirections in one place. Ideally, you could use a ready library. UrlRewriteFilter can do the trick, as long as you're fine with a BSD license (check out their google code site for details)
Another option is to handle this with a proxy set up in front of your Java app. You can set up an Apache server to offer basic caching and rewrite rules without complicating your Java code.
As explained in Tom's answer, you can not use more than one #Path annotation on a single method, because you will run into error: duplicate annotation at compile time.
I think the simplest way to get around this is to use method overloading:
#Path("{foo}")
public Response rest(#PathParam("foo") final String foo) {
return this.rest(foo, "");
}
#Path("{foo}/{bar}")
public Response rest(#PathParam("foo") final String foo,
#PathParam("bar") final String bar) {
return Response.ok(foo + " " + bar).build();
}
You could also use more different method names if you run into the case where multiple overloaded methods have the signature.
Another solution for your particular example:
http://a/b/c
http://a/b
Let's suppose that:
/a is for the resource class
/b/c and /b are the paths for the methods
because a full path looks like:
<protocol><host><port><app><url-pattern><resource-path><method-path>.
Use optional parameter
#Path("/b{c : (/c)?}")
public Response searchNames(#PathParam("c") String val) {
...
}
The example above works for all examples like:
/b
/b/
/b/c
/b/c/
but when c is provided, the val is /c (it has a / before).
If you want to fix the problem above (to avoid Java parsing), you need something more complex:
#Path("/b{slash : (/)?}{c:((?<=/).*)?}")
which will return only c (not /c) for the 3rd bullet point, but for the 4th bullet point it will return c/ which has to be parsed in Java.
But for your case ("the method executed is the same"), don't worry about parsing because you don't have different actions.
If you are using Spring then try
#RequestMapping(value = {"/def", "/abc"}, method = RequestMethod.POST)
This will work for both /abc and /def.
– sSaroj Nov 17 '17 at 10:13
Is there's a way to get the value returned by a java controller method in javascript in the views ?
what I want to do is:
i'm in view showX rendered by controller method X.show()
i want to create an object y so $.post('#{Y.create()}')
now i need the id of the created object of type y to use it in the same view (showX).
is that possible?
It sound like what you need (although your question is very vague), is to return JSON from your controller method.
Such as, in your controller, you can do
public static void myActionOne() {
renderJSON(myObject);
}
And then you will call myActionOne from your javascript using $.post. I would also suggest looking at the Play jsAction tag if you are not already using it. This will return a JSON representation of the object. You can then take whatever information you need and call a second action in the same way.
Again, in the second action, I would suggest jsAction, as it makes passing parameters into your actions far easier.
EDIT:
Based on your edit, then all you need to do is in your controller method Y.create, do something like
public static void create() {
MyObject obj = new MyObject();
obj.save();
Long id = obj.id;
renderJSON(id);
}
Obviously your code to create your object will be different, but you get the idea. You can then just take the data from the JQuery post response, and access the id that has been returned, using standard javascript.
You question is too vague. But you probably will need of AJAX to get a value of this kind.
Take a look here: http://www.oracle.com/technetwork/articles/javaee/ajax-135201.html