Using Jersey, what is the RESTish way to this, Should I create a method starting with "update" like this, or I should create a subresource (or whatever Jax-Rs thing) under accountseetings path? Or should I simply use the same method name with different verbs?
#GET
#Path("/accountsettings")
public Settings accountSettings() {
}
#PUT
#Path("/updateaccountsettings")
public void updateAccountSettings() {
}
In REST verbs define what you are doing and URLs define what you are doing it to.
So here a PUT to /accountsettings would seem to be the normal way to do it.
Calling the method updateAccountSettings() seems to make sense.
Related
I have several APIs which retain a parameter "feature" from the url (path param). To avoid retrieving it in each method endpoint (eg.)
#GET
public void findAll(#PathParam("feature") String feature);
am trying to implement AOP using AspectJ.
Following is the implementation of the Aspect
#Aspect
public class FeatureAOP {
#Pointcut("execution(* x.y.z.rest.ModifiersFacadeWrapper.*(..)) && args(feature)")
public void pointCut(String feature) {
}
#Before("x.y.z.rest.aop.FeatureAOP.pointCut(feature)")
public void parseParams(JoinPoint jp, String feature) {
Object[] x = jp.getArgs();
System.out.println("Feature: " + feature);
}
}
The above method gives me the value of "feature" in the Aspect class but if I change the method findAll to following signature, it doesn't works.
#GET
public void findAll();
What I understand is the control is transferred to the Aspect after the parameters are resolved and removing it from the method definition is failing it.
Doing so, thus takes me to the same point where I have to define all method endpoints with the parameter in its signature. I would like to know if there is a way I can get the PathParams in the Aspect class without having to define my methods with the designated parameters.
I think you could probably do it by putting the resolved params in a globally accessible data structure (e.g. a Singleton having some sort of Map or Set), but
I wouldn't recommend that kind of approach. I don't know why you don't like having all the params in your method signatures, but that is the intended way of declaring rest services, e.g.
#GET
#Path("{feature}")
#Produces("text/plain")
public String getFeature(#PathParam("feature") String feature) {
return feature;
}
This way you don't have to write any code for retrieving the params, the rest library you are using (be it Jersey or a different one) will just do everything for you.
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 ;)
In short:
I'd like to return different JSONs, with say less attributes, when a request comes from a phone than when it comes from a desktop pc.
I want to build a REST service.
The service will serve data based on JPA entities.
The service is declared with #Path.
Depending on the User-Agent header, I want to serve a richer JSON for desktop than for mobile devices. Selection to be done serverside.
Is there a better way than to build a second serializer and use a condition (if(request useragent ) to call them (in every method) and be forced to return some String instead of any Object (making #Produces annotation unused).
Thank you
One way it to add a PathParam or QueryParam to the Path to tell the device type in the request, so the service can be able to understand the type of device, from which the request is and create the appropriate JSON.
Please check the most voted SO answer to find out whether the request is from mobile or desktop and add the parameter accordingly
You can use jax-rs resource selector, which will use different sub-resource depending on user-agent string.
#Path("api")
public UserResource getResourceByUserAgent() {
//the if statement will be more sophisticated obviously :-)
if(userAgent.contains("GT-I9505") {
return new HighEndUserResource();
} else {
return new LowEndUserResource();
}
}
interface UserResource {User doSomeProcessing()}
class HighEndUserResource implements UserResource {
#Path("process")
public User doSomeProcessing() {
//serve
}
}
class LowEndUserResource implements UserResource {
#Path("process")
public User doSomeProcessing() {
//serve content for low end
}
}
By invoking "/api/process" resource the response will depend on userAgent. You can also easily extend the solution for other devices, and implement MiddleEndUserResource for example.
You can read more information about sub-resources here:
I've had a nice 'ride' with RESTful technology. I am using a Hello.java resource like this:
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
}
With this I can access my resource with the path http://my.host/res/hello . I want to 'ride' RESTful even harder. Having this one resource path is a bit boring.
PROBLEM
I would like to have a dynamically created resources like this:
http://my.host/res/hello
http://my.host/res/hello/1
http://my.host/res/hello/2
...
http://my.host/res/hello/999
It doesn't make sense to create a .java resource for every #Path("/hello/1") ... #Path("/hello/999"). Right? Probably this list of sub-resources could be even bigger or dynamically change in time. What is the solution for that?
Thanks.
You can use the #Path annotation on methods inside your Resource class.
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
#GET
#Path("{id}")
public String myMethod(#PathParam("id") String id) {...}
}
The paths will be concatenated so it will match /hello/13. The {id} is a placeholder for the actual value entered, which can be retrieved with #PathParam. In the previous URI, the String id will have the value 13.
You will have to use PathParam feature for REST URI. http://docs.oracle.com/javaee/6/api/javax/ws/rs/PathParam.html
#Path("/hello/{id}")
public class Hello {
}
I have a class this is annotated with #Path like so:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit"))
{
//This class returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id)
{
//This class returns a single widget by id
//...}
When I fire up a test client the localhost/widgets maps as expected, but when the getWidgetById method is mapped to localhost/widgets/widget/{id}. This is not what I want - I would like to have localhost/widgets and localhost/widget/{id}
I have tried omitting the #Path annotation at the class level, but that prevents Jersey from recognizing this class as a REST Resource (I tried both the ScanningResourceConfig and the ClassNameResourceConfig - both failed to load the class as a resource unless there was a #Path at the class level).
I guess a (ugly) workaround would be to split the methods between classes a WidgetResource class and a WidgetsResource class. I think this is a terrible solution since both of these methods share resources in the same class, but I really need the REST-ful localhost/widget (for a single entity) and localhost/widgets (for plural).
Am I missing something - is there some way for me to have Jersey pick up the class as a Resource class if I just #Path annotate the methods (I couldn't get it to work), if not can I force absolute mapping (#Path(/widget/{id})) or some relative mapping (#Path(../widget/id) - neither of those work in reality - just an analogy of what I'm after. Thanks!
This part is about what you need:
Personally, I find your mapping strange and confusing. Just keep it like this:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
// it's also possible to limit the number returned by
// using a query parameter. You could easily implement
// pagination by adding further query parameters like
// 'offset', 'sortOrder', etc.
//...
}
#GET
#Path("{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}
It seems natural to append the path to a collection with an ID to fetch an object from the collection. There's really no need to make it widgets/widget/{id}. The widget part is obvious and unnecessary.
Here's a really neat tutorial on RESTful APIs: "Teach a dog to REST" by apigee I think it's a really good video. The authors make a couple of good points. And here's a link to a longer version of the same presentation
This part is about what you want:
If you really want to keep the plural/singular dualism (which I really don't recomment), you can annotate your code like this:
But it's really ugly
#Path("/")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
#Path("widgets")
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}
My suggestion is to have your paths be:
"widgets" and "widgets/id/{id}". Or if you knew you were never going to query by anything other than id, your second one could simply be "widgets/{id}".
I would not switch between plural and singular in your path. Since you accessing the same type of resource for both, your root should be the same. The second form just specifies it more -- a vectoring-based approach for getting more specific.