Is it possible to use RestEasy's Path annotation to get the following string:
/items.json
I was thinking something like this: /items{(\.)?format}, where format could be json, xml etc.
I would then have a method with an argument like: #PathParam("format") String format.
Thanks.
I managed to make the following work with my use case: item{format:(\.(json|xml))?}
I chose to make the reg exp restrictive so as not to have to handle unsupported or invalid formats inside the actual service method, but if one prefers a more general approach I think that instead of (json|xml) one can add \S+.
you might want to create two methods, one for the default type and one for the optional types but yes, your logic should work:
#Path(items.{format})
public Response getItems(#PathParam("format") String format) {
}
#Path(items)
public Response getItems() {
return getItems("json");
}
Related
How can I distinguish in java graphQL if a parameter was explicitly set to null, or if it was not provided at all?
The use case that I try to achieve is the following: I have a mutation like this
updateUser(id:Int!, changes:UserChanges!)
#where UserChanges is defined as:
type UserChanges {
login:String
email:String
#etc
}
The idea here is that the user provides only the fields that he wants to change (like react setState for example).
So if email is ommited, I want to leave it unchanged.
But what if he/she wants to explicitly set email to null?
Is there any way figure this out from my resolver method and act accordingly?
(I use graphql-java-tools library)
I found the answer. In case somebody needs it:
An instance of graphql.schema.DataFetchingEnvironment is available to every resolver method.
This provides methods like getArguments(), hasArgument() etc.
Using those methods, we can find out if an argument was an explicit set to null, or if it was not provided at all.
Looks like deserialization from query/variables is handled by fasterxml Jackson, and that's proper place to deal with the issue, otherwise it becomes too complex: check every field? nested?
So: UserChanges.java should look like this:
class UserChanges {
// SHOULD NOT HAVE ALL ARGUMENT CONSTRUCTOR!
Optional<String> login;
Optional<String> email;
... getters & setters
}
in this case deserializer will use setters, ONLY FOR PROVIDED FIELDS!
And {"login":null} will become:
UserChanges.login = Optional.empty
UserChanges.email = null
I'm going to implement a RESTful webservice using Spring.
Let it be an ordinary PUT method, something like this:
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
In such a case input JSON format (if it corresponds to Foo class) will be successfully converted to Foo instance with no extra efforts, or error will be issued in case of wrong format.
But I'd like to make the service able to consume two different types of formats using same method (e.g. PUT) and same URL (e.g. /foo).
So that it possibly looked like:
//PUT method #1
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
//PUT method #2
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody FooExtra fooExtra) {
fooService.update(fooExtra);
}
and Spring converter tried to convert input JSON not only in Foo but in FooExtra as well and invoked corresponding PUT method depending on input format.
In fact, I tried to implement it exactly as it described above but without success. Is it even possible? Maybe, I need some kind of "trick"?
What is the best (and the most proper) way to achieve such behavior? Of course, I could always make two different URLs but I'd like to know whether it is possible with the same one.
Your attempt didn't work simply because Spring tried to match your methods against the request, by looking at url and method type, which are in both cases the same. It does not work like overloading in Java; argument types do not differentiate your methods.
But there are good news. SpringMVC can also examine request headers and request parameters when trying to match your handler methods. Since what you want to pass is actually pure metadata -an alternative format type of the same information- it makes perfect sense to use a custom request header. It's very easy to add custom headers when using a rest api. See the following link for JAX-RS: Adding a custom header.
Now in your server side you should configure the handler methods as:
//PUT method #1
#RequestMapping(method=RequestMethod.PUT, value="/foo", headers="returnType=Foo")
public #ResponseBody Foo updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
//PUT method #2
#RequestMapping(method=RequestMethod.PUT, value="/foo", headers="returnType=FooExtra")
public #ResponseBody FooExtra updateFoo(#RequestBody FooExtra fooExtra) {
fooService.update(fooExtra);
}
Note also that if you want to access a return value with #ResponseBody you have to return your object, otherwise make the methods void
For understanding it we should think how Spring works, it uses Dispatcher Servlet. I don't think that spring does "combine" work for different types of input.
So my answer will be: "trick" with two different urls ;)
Is there a way to tell Spring to map request to different method by the type of path variable, if they are in the same place of the uri?
For example,
#RequestMapping("/path/{foo}")
#RequestMapping("/path/{id}")
if foo is supposed to be string, id is int, is it possible to map correctly instead of looking into the request URI?
According to the spring docs it is possible to use regex for path variables, here's the example from the docs:
#RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(#PathVariable String version, #PathVariable String extension) {
// ...
}
}
(example taken from http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-patterns )
Judging from that, it should be possible to write something like this for your situation:
#RequestMapping("/path/{foo:[a-z]+}")
#RequestMapping("/path/{id:[0-9]+}")
JAX-RS/Jersey allows URL path elements to be converted to Java method arguments using #PathParam annotations.
Is there a way to convert an unknown number of path elements into arguments to a vararg Java method? I. e. /foo/bar/x/y/z should go to method: foo(#PathParam(...) String [] params) { ... } where params[0] is x, params[1] is y and params[2] is z
Can I do this in Jersey/JAX-RS or some convenient way?
Not sure if this is exactly what you were looking for but you could do something like this.
#Path("/foo/bar/{other: .*}
public Response foo(#PathParam("other") VariableStrings vstrings) {
String[] splitPath = vstrings.getSplitPath();
...
}
Where VariableStrings is a class that you define.
public class VariableStrings {
private String[] splitPath;
public VariableStrings(String unparsedPath) {
splitPath = unparsedPath.split("/");
}
}
Note, I haven't checked this code, as it's only intended to give you an idea.
This works because VariableStrings can be injected due to their constructor
which only takes a String.
Check out the docs.
Finally, as an alternative to using the #PathParam annotation to inject a VariableString
you could instead wrap this logic into your own custom Jersey Provider. This provider would inject a "VariableStrings" more or less the same manner as above, but it might look a bit cleaner. No need for a PathParam annotation.
Coda Hale gives a good overview.
is it possible to configure GET method to read variable number of URI parameters and interpret them either as variable argument (array) or collection? I know query parameters can be read as list/set but I can't go for them in my case.
E.g.:
#GET
#Produces("text/xml")
#Path("list/{taskId}")
public String getTaskCheckLists(#PathParam("taskId") int... taskId) {
return Arrays.toString(taskId);
}
Thanks in advance
If I understand your question correctly, the #Path annotation can take a regular expression to specify a list of path components. For example, something like:
#GET
#Path("/list/{taskid:.+}")
public String getTaskCheckLists(#PathParam("taskid") List<PathSegment> taskIdList) {
......
}
There's a more extensive example here.
I am not submitting this as an answer as it is merely an edge case on the currently accepted answer which is what I've also used.
In my case (Jersey 1.19) /list/{taskid:.+} would not work for the edge case of zero variable parameters. Changing the RegEx to /list/{taskid:.*} took care of that. See also this article (which seems to be applicable).
Moreover, upon changing the regexp to cardinality indicator to * (instead of +) I also had to deal programmatically with the case of empty strings as I would translate the List<PathSegment> into a List<String> (to pass it into my DB-access code).
The reason I am translating from PathSegment to String is that I didn't want a class from the javax.ws.rs.core package to pollute my Data Access Layer code.
Here's a complete example:
#Path("/listDirs/{dirs:.*}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listDirs(#PathParam("dirs") List<PathSegment> pathSegments) {
List<String> dirs = new ArrayList<>();
for (PathSegment pathSegment: pathSegments) {
String path = pathSegment.getPath();
if ((path!=null) && (!path.trim().equals("")))
dirs.add(pathSegment.getPath());
}
List<String> valueFromDB = db.doSomeQuery(dirs);
// construct JSON response object ...
}