I have a service method which looks like this
public void deleteData(Data data) {
this.dataDao.deleteData(data);
}
Data class have several fields in it. Somethig like this
private String name;
private String category;
private String discriminator;
private String description;
private String appName;
// getters & setters
I need to write a rest method for this. I was thinking to write something like this
#DELETE
#Path("/deleteData")
public Response deleteData(Data data) {
// implementation
}
The problem is that using #DELETE with entity body is not recommended or widely used.
My question is if it's ok to use #PUT instead of #DELETE? I can't change the service method implementation so that's not an option. What's the next best alternative here?
UPDATE
In dataDao.deleteData() method, finding an object is not done by object's ID. It looks something like this:
DataEntity entity = this.findDataByNameAndAppName(data.getName(), data.getAppName());
I decided to do something like this:
#DELETE
#Path("/deleteDataset")
public Response deleteDataset(#QueryParam("name") String name,
#QueryParam("appName") String appName) {
// implementation...
}
I didn't find any example of #DELETE method with #QueryParam, though. All examples was using #PathParam instead.
Well, DELETE is meant for... deleting stuff. So stick to that (without body).
You could either delete a resource using its unique identifier sent as a path parameter:
DELETE /resources/{id} HTTP/1.1
Host: example.org
If you need to delete multiple resources, you could consider query parameters to filter a collection of resources and then delete the resources that match such criteria:
DELETE /resources?name=foo&category=bar HTTP/1.1
Host: example.org
Related
If I have a Get request that returns orders of clients, how can I filter the response to give me the objects that have a specific value for example that are made by those specific clients in Spring Boot?
I have tried with #PathVariable and #RequestParams but every attempt failed.
Thank you in advance.
If you want to show a specific order which has an identifier of some sort, use #PathVariable. In the following example, the identifier is a String, but in many case it will rather be long or an Integer.
#RestController
#RequestMapping("/orders")
public class OrdersController {
#GetMapping("/{id}")
public Order getOrder(#PathVariable("id") String id) {
// get the order with a specified id from the backend
}
}
The web request in this case will look like http:/<host>:<port>/orders/123
If you want to filter the order by some name, like 'madeBy John', use Request parameter:
#RestController
#RequestMapping("/orders")
public class OrdersController {
#GetMapping("/")
public List<Order> getOrdersFilteredByName(#RequestParam("madeBy") madeBy) {
// get the order filtered by the person who made the order
// note, this one returns the list
}
}
In this case the web request will look like this: http:/<host>:<port>/orders?madeBy=John
Note that technically you can implement whatever you want at the backend, so you can pass, say, John in the first example as a path variable, on server its a String after all, however what I've described is a straightforward and kind-of-standard way of doing these things - so can expect to see this convention in many projects at least.
#RestController
#RequestMapping("/order")
public class OrderController {
// http://<host>:<port>/order/1
#GetMapping("/{id}")
public Order getOrder(#PathVariable Long id) {
// Return your order
}
// http://<host>:<port>/order?madeBy=John
#GetMapping("/)
public List<Order> getOrdersMadeBy(#RequestParam("madeBy") String madeBy) {
// Return your order list
}
}
Day 1: Added below rest endpoint for delete operation.
#Path("/company/v1/department")
#Component
public class ManageResource {
#DELETE
#Path("/{identifier}/{identifier_value}/employee")
public void delete(#PathParam("identifier") String identifier,
#PathParam("identifier_value") final String identifierValue,
#QueryParam("age") final String age) {
//delete operation
}
}
I was able to invoke DELETE endpoint using postman with below request:
DELETE: http://localhost:8080/company/v1/department/name/baner/employee?age=50
Day 2: Added below rest endpoint for the update operation in the same resource.
#Path("/company/v1/department")
#Component
public class ManageResource {
#DELETE
#Path("/{identifier}/{identifier_value}/employee")
public void delete(#PathParam("identifier") String identifier,
#PathParam("identifier_value") final String identifierValue,
#QueryParam("age") final String age) {
//delete operation
}
#PUT
#Path("/empid/{value}/employee")
#Consumes(MediaType.APPLICATION_JSON)
public void update(#PathParam("value") final String identifierValue,
#RequestBody final EmployeeUpdateRequest request) {
//update operation
}
}
After adding this new endpoint, I am able to invoke PUT using postman with below request:
PUT: http://localhost:8080/company/v1/department/empid/epid-123/employee
{
//Json request body
}
But when I try to invoke Delete endpoint it is giving me 405 (Method Not Allowed) error.
If I comment my new Put method, then the Delete method works fine.
Also, if I replace Path for Put method to "/{identifier}/{identifier_value}/employee" then both Delete and Put method works fine.
I am using Jersey 1.19 with tomcat.
Can someone help me with this?
Your Paths are in conflict with each other. Let me try to explain:
DELETE = /{identifier}/{identifier_value}/employee
PUT = /empid/{value}/employee
That means when we evaluate the path from left to right, we can either have
{identifier} which is anything or
"empid" which is a fixed string
Jersey always tries to find the "most perfect" match for a REST endpoint. It does so by evaluating the path from left to right.
Fixed strings always take precedence before random variables!
Basically that means when you want to call a DELETE, you cannot have the value "empid" for the variable "{identifier}" because then you are already out-of-scope
So the DELETE call to
http://localhost:8080/company/v1/department/empid/empid-123/employee
will not work as Jersey had to make a decision whether "empid" in the request matches "{identifier}" (DELETE) or "empid" (PUT). And as i tried to explain above, fixed strings take a higher priority.
In contrast, any other DELETE request where
http://localhost:8080/company/v1/department/{identifier}/empid-123/employee
and
{identifier} != "empid"
works.
Possible solution:
make your rest endpoints resource-oriented
DELETE:
/employee/{employee-id}
PUT:
/employee/{employee-id}
Notice how the endpoints are identical, since other than the ID in most systems, no information is needed to identify an entity.
I am using Jersey and converting my existing data services into RESTful data services. Most simple GETs and PUTs I could successfully convert. But following are some I am not able to convert:
X Get (T) // for complex queries with complex result
X Post (T) // for creating with complex result
X PUT (T) // for updating with some success message object
where T and X are a complex objects
I have tried #queryparam, #pathparam with complex objects on GET with #consume & #produce and those didn't work. Also tried POST (though I really needed GET) with url encoded and that didn't work too.
Please Help. I am in need of sample code that does it.
REST isn't designed to handle complex queries as the query is actually the URL. When you retrieve a resource you specify the ID of the resource you want. This is simply a number or string and is easily represented in the URL for example;
http://host/employee/57
would get you employee 57. If your requirements are more complicated then you might want to use a search method, where you pass several parameters. You could use #QueryParam here but this isn't really REST in a pure form.
If you are POSTing or PUTting data then you use the same URL as you would if you were doing a GET, only this time you send data in the content body. As you are able to serialize the object in order to return it to a GET request your client should also be able to serialize it to send it to you in a PUT or POST.
Here's an example of a GET and POST;
#XmlType
public class Employee {
private int id;
private String name;
//getters and setters
}
#Path("/employee")
public class EmployeeService {
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_XML)
public Employee get(#PathParam("id") String id) {
Employee e = employeeDao.getEmployee(id);
if (e != null) {
return e;
} else {
throw new WebApplicationException(404);
}
}
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Employee post(Employee employee) {
return employeeDao.insertEmployee(employee); //Assumes your DAO sets the ID
}
}
I am working on creating a RESTful API for a project. I am facing a few problems trying to implement it with jersey:
My object model does not contain uri info obviously. e.g, lets say I have a Fruit class. Fruit object would have let's say a FruitName and a FruitColor. But in the response I also need to send a URI. How is this usually handled? Should I create a separate "FruitResource" that has a constructor which takes a "Fruit" and creates a full resource from it, including URI? I need URIs in the nested objects as well, e.g if I am returning a list of Child objects, I need each Child object to also have a URI, but I donlt want the URI to be part of the object model. What is the cleanest way to do this?
I want to have capability to return full and partial views of the same resource. Partial views would just have the name and the URI for example. How to get this done?
Right now what I have is a Service class that accepts the requests, which uses the DAO to create and return the objects as they are modelled from the DB, serialized to JSON using jackson.
There i a way to use JaxB class and you can pass Object Model to JaxB class and JaxB class generates URI.
The below is small prototype.
UserResource Class
#Path("/user")
public class UserResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{user-id}")
public UserJaxB getUser(#PathParam("user-id") String userId, #Context
HttpServletRequest request) {
// now XYZ is hard-coded value
String serviceEndpoint = request.getContextPath() + "/" + "user";
UserModel userModel = new UserModel(userId, "XYZ");
return new UserJaxB(serviceEndpoint,userModel);
}
}
User JAXB Class
#XmlRootElement
public class UserJaxB {
private String name;
private String id;
private String serviceEndpoint;
private String URI;
public UserJaxB(String serviceEndpoint, UserModel userModel) {
this.name = userModel.getName();
this.id = userModel.getId();
this.serviceEndpoint = serviceEndpoint;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getURI() {
return this.serviceEndpoint + "/" + id;
}
}
User Model Class
public class UserModel {
String name;
String id;
public UserModel(String name, String id) {
this.name = name;
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
I'm working on a project that supports both of those concerns: https://github.com/skyscreamer/yoga with more information at http://yoga.skyscreamer.org/, including a demo.
It replaces the JAX-RS (or Spring MVC) rendering mechanism with a custom built solution that's more in tune with the needs of a REST System.
For #1, we have an annotation that you have to apply to your Child pojo. JAX-RS's annotations are meant to map URLs to controllers not to the underlying object, so we needed a custom solution... but it's really just comes down to 1 additional annotation per pojo.
For #2, we have a URL based mechanism of specifying which additional information you want from a pojo or a pojo's children (and can be nested further).
I hope this helps.
1) I'm not aware of any Jersey or JAX-RS mechanism supporting this. Seems like a bad practice to have to add the URI to the constructor for each of your domain classes, though. You could create an aspect that would intercept the method and wrap the response in a new object - adding the URI of the resource in the wrapper (you could get the URIInfo via reflection from the interceptor). I've done this when building etag support so I don't have to add cache code to every response. I suppose you could also add something in the same aspect to handle the child URI issue...
You might also want have a look at these dicussions:
http://java.net/projects/jersey/lists/users/archive/2009-01/message/357
http://markmail.org/search/?q=list%3Anet.java.dev.jersey.users+brett.dargan%40gmail.com#query:list%3Anet.java.dev.jersey.users%20brett.dargan%40gmail.com+page:1+mid:7ln7wixfihfodngg+state:results
2) For building "lighter" response entities I typically have a BeanLite.class with just the properties I need for a summary and then a Bean.class extending it with more detail. You can add both to your ORM and provide an option to switch representations in your DAO.
Thanks for all your responses. Going through all the approaches you guys presented and after a little bit of research on my own, this is what I settled on:
1) I am adding uri as part of the object model. This seems to be the cleanest solution to me currently. The URI can be automatically populated whenever the object is created (using other properties of the object). Earlier I thought this is a bad idea, but I am not able to foresee any problems with this approach other than the extra field that will have to keep moving with the objects.
2) For supporting full/partial views, I am trying to use the #JsonView annotation. This seems to be a good approach.
Let me know if there are any other potential issues with this way of handling things.
So I am new with restlet. I am creating a Android application that can communicate with a GAE server (with objectify DB)
I Did this very good tutorial to learn:
http://www.tutos-android.com/webservice-rest-android-appengine-restlet-objectify
It's working very well but do very little.
Onely 2 methods:
public interface UserControllerInterface {
#Put
void create(User user);
#Get
Container getAllUsers();
}
For my application its more complicated so I add many more methods:
public interface UserControllerInterface {
#Put
public void createUser(ObagooUser user);
#Put
public void createMessage(ObagooUser user, String message);
#Put
public void updateMessage(ObagooMessage message);
#Get
public List<ObagooUser> getAllUser();
#Get
public ObagooUser getUserById(String id);
#Get
public List<ObagooMessage> getAllMessage();
#Get
public List<ObagooMessage> getAllMessageFromSender(ObagooUser sender);
#Get
public ObagooMessage getFreeMessage(ObagooUser user);
}
Each of these mothds working server side (I tested with Junit).
Now I am coding the android part and I am having problems.
When I do a simple call to getAllMessage() I get an error:
java.lang.IllegalArgumentException: id cannot be zero
at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:44)
at com.googlecode.objectify.ObjectifyFactory.typedKeyToRawKey(ObjectifyFactory.java:269)
at com.googlecode.objectify.impl.ObjectifyImpl.find(ObjectifyImpl.java:159)
at com.googlecode.objectify.impl.ObjectifyImpl.find(ObjectifyImpl.java:183)
at com.obagoo.dao.ObagooUserDAO.getUserById(ObagooUserDAO.java:43)
at com.obagoo.controller.ObagooController.getUserById(ObagooController.java:47)
It's going in the wrong method (getUserById).
I put a break point in my getAllMessage and it's going in, but it is also going in other methods.
If I test many times, sometimes it's calling, createUser or another random method.
Do you see what I am doind wrong?
Adding the getAllMessage code:
public List<ObagooMessage> getAllMessage() {
// logger.debug("Getting all Obagoo Messages");
List<ObagooMessage> msg = new ArrayList<ObagooMessage>();
Objectify ofy = ObjectifyService.begin();
Query<ObagooMessage> q = ofy.query(ObagooMessage.class);
for (ObagooMessage u : q) {
msg.add(u);
}
return msg;
}
In the examples that I've seen, its always shown that you should separate the controller/resource handling the URI for the list resource from the single item (id/name based) resource. So you would have something like:
router.attach("/users", UsersController.class);
router.attach("/users/{id}", UserController.class
router.attach("/messages", MessagesController.class);
Notice the plural naming on the first class: UsersController, and singular naming on the the second class: UserController. The first class would handle cases where no id was being provided, such as a get of all users. Also, note when the id is provided in the URI, it can be automatically mapped into an id field on the class. So the Get method has no parameters on the method call.
As for handling a subset, then for messages from a specific user, that could be handled with query parameters. For instance when calling via a URI with /messages?sender=id, the MessagesController.class would use the following in the method handling the Get:
Form queryParams = getRequest().getResourceRef().getQueryAsForm();
String id = queryParams.getFirstValue("sender");
Hope that helps. I'm no expert, so anyone feel free to correct me.
As error says: you are creating a key with zero Id.
My gues is that your ObagoMessage Id field is long? You should make it Long. Primitive long Id values are not autogenerated, while object type Long are. See the docs.