Reusing the same Domain Object in For Get and POST - json

Assuming I have a student Object
public class Student{
private long id;
private string name;
private List<string> courses
}
In a typical Get request, to get a student I send the Student object to the client.
In the case of a PUT request to modify the Student object by adding or removing a course or a POST request to create a Student record, I only need to receive from the client the student.id and the list of courses.
My question is, Can I send back the same Student object from the client in the PUT or POST request without including the name or maybe have name=null?
Or should I create a separate domain object that will be sent by the client for example:
public class StudentReponse
{
private long id;
private List<string> courses;
}
I guess my generic question is, should we separate the Request and response objects in Rest API? or for code re usability try to use the same domain objects for both directions?

should we separate the Request and response objects in Rest API?
Yes - it allows to evolve both the request and response independently.
If you follow REST practices when a create is issued, you should return 201 - created and the ID of the newly created object.
If the client requires details about it, the client can use the ID + GET to get the full resource representation.
Also consider not exposing the domain objects directly through REST.
For example having a domain entity object - it will probably have some fields related to persistence layer like database Id, createdOn, createdBy - etc. Those fields should not be sent to the client. Use a simple StudentDto (StudentResponse, StudentResponseDto whatever you want to call it) representation, which holds only those fields, which are of interest to the client.
Keeping domain and response objects separate also gives you the ability to evolve them separately or change the data representation.
Imagine you have a JPA Entity and you use both JPA and Jackson annotations in the same class - it's very messy and hard to read and maintain.
Update:
If you're using the same object to be modified by the client and sent back to the server, I guess you could reuse it and model it like this:
get
#GetMapping("/students/{id}")
public StudentDto getStudent(#PathVariable long id) {
return studentService.get(id);
}
update (replace)
#PutMapping("/students/{id}/")
public ResponseEntity updateStudent(#PathVariable long id, #RequestBody StudentDto student) {
return new ResponseEntity(studentService.replaceStudent(id, student), HttpStatus.OK);
}
OR
update (partial update)
#PostMapping("/students/{id}/")
public ResponseEntity updateStudent(#PathVariable long id, #RequestBody StudentDto student) {
return new ResponseEntity(studentService.updateStudent(id, student), HttpStatus.OK);
}

Related

Object modelling - REST vs Hibernate

I am facing this in a project of my own, but i am sure this should be a generic issue.
I am trying to build rest services using java, spring and hibernate.
My typical entity object is modeled like below
#Entity
public class Company implements Serializable{
private Long companyId;
private String name;
private String shortDescription;
private Long logoId;
private Set<ActivityType> activityTypeList;
private String address;
private RegistrationInfo regInfo;
}
Where one object has associations with other objects, so my model object contains references to the associated objects.
I use jackson configured with spring for json-object and object-json conversion, it works perfectly, so I am good till this point.
Now I want my POST calls(which I am using for object creation) to create Company objects, only catch here is that I do not want to pass complete json for contained objects(like ActivityType and RegistrationInfo in above example), rather an id for each where the object with provided id already exists in the database. For example see this JSON snippet:
{
"companyId": 5,
"name": "test company created by rest call",
"shortDescription": "dummy description",
"logoId": 0,
"activityTypeList": [
{"activityTypeId": 1}
],
"address": "202, kardoga lane, lonavala, Maharashtra",
"regInfo": {
"registrationInfoId": 1
}
}
My rest service just needs to associate the newly created company object with the existing contained object with the provided id(inside POST call's body).
Jackson serialization creates unpopulated objects in this case, which hibernate fails to persist (for obvious reasons, there is no data just an id)
Sure i can use a DTO over here, i can get REST -> DTO and then DTO -> entity, but that seems an overhead and moreover in order to populate the Company object defined above, I will need to fetch the associated objects first from DB, attach them to my company object and then save it back. That will be an unnecessary DB trip.
Anyone has better idea on this one? Any design pattern, framework or simple tips/tricks in which i dont require a dto as well as save DB calls.

Hibernate retrieved objects to Json using GSON

I have developed this webapp using Spring MVC + Hibernate.
I retrieve all my objects in a Service then return them directly to the controller. These objects generally are lazily initialized so collections are empty.
So for object User:
User
{
int idUser;
City city;
String name;
List<User> friends;
}
I return an object with just idUser and name, City and Friends are not initialized.
I want to take advantage of all my services methods (without modifying them) to provide a REST api, so if from my ApiController I request to get user with id 1, I retrieve all useful information about this user in JSON.
I tried using GSON but as soon as it tries to jsonize the city object it crashes because it has been lazily initialized. Same goes for the friends collection.
For collections it's not much of a big deal since in my api I would have another request url where you can get all friends given a user Id, but in the case of relationships with a single object (like the city in this example), I would like to return the id of the City which by definition of Lazy loading is indeed set.
How can I tell GSON to jsonize just the cityId attribute of City instead of the whole object?
Will nulling the rest of collections be a good solution so they're not converted into JSON? Is there any other way to explicitly tell GSON to ignore these attributes?
I believe you need to put your gson.toJson(...) within a transaction, in springMVC typically #Transactional at controller method where you are doing the actual serialization will do.
If you really want to skip fields or selectively serialize fields of using Gson, you can check https://stackoverflow.com/a/3341439 for gson exclusion strategy. You can skip based on Field Annotation or field name or the entire referenced class.
Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return <class exclusion logic, return true for exclusion else false>;
}
public boolean shouldSkipField(FieldAttributes f) {
return <field exclusion logic>;
}
}).create();

How configure rest service and rest client to serialize java.util.Date when date is value in the Map<String,Object>

I am using Jersey rest service. My resource object takes a Object as shown below as post parameter. From the client side
I am setting key values pair in properties field of my input object. One of the k,v pairs were (key,value)=("timestamp",new java.util.Date()). Once I have InputObject ready, I use json to serialize the data and send to RestService
when the request is received by my rest resource, I am getting the value corresponding to the key timestamp as a long.
I wanted to know if it is possible some how to get back value as java.util.Date e.g some kind of configuration or overridding capability in jersey
I am interested in standard way of handling this problem, there are few hacks that I know, I would apply them if only std way is not possible to achieve my requirement
public class InputObject {
private long id,
private Map<String,Object> map;
// not adding the getters and setters
}
Following is what my resource object accepts.
#POST
#Path("/forceevent")
#Consumes("application/json")
#Produces("application/json")
public AckValue sendEvent(InputObject request)
{
return AckValue.FAILURE;
}

Pass object as parameter in GET request using Google Http Client

I'm using Google Http Client and Jackson to query data to backend (JSON API).
I need to pass parameters (one Java bean object). The object might have few or lot of field. Initially I attempt to pass it as content as follow:
HttpRequest request = requestFactory.buildGetRequest(getUrl(api)).setContent(new JsonCContent(jsonFactory, params));
However, I'm not allowed to set the HTTP content in GET operation.
Any suggestion how can I pass these parameters?
Under one condition:
I don't want to write a util method to convert this object into string of URL parameters. But if there's already reusable API to do it, that would be fine.
I need generic solution if possible. Because I'm going to apply this to 600 JSON API calls.
My last alternative would be to change backend to expect POST request instead of GET, then I perform POST operation on the client side.
Thanks
Instead of extends GenericUrl, you can use GenericUrl.put (inherit from GenericData) to set query parameters. For example:
GenericUrl genericUrl = new GenericUrl("http://yourapi.com/request");
genericUrl.put("user", "user name");
genericUrl.put("token", "token values");
HttpRequest request = requestFactory.buildGetRequest(genericUrl);
It seems like the expected usage is to extend the URL class you are using for your buildGetRequest() call. For instance, let's say you wanted to provide two extra query parameters called "user" and "token". You could do this with the following:
HttpRequest request = requestFactory.buildGetRequest(
new CustomUrl("http://www.yourserver.com").setUser(userId).setToken(token));
where the CustomUrl class is defined as:
public class CustomUrl extends GenericUrl {
public CustomUrl(String encodedUrl) {
super(encodedUrl);
}
#Key("user")
private String mUserId;
#Key("token")
private String mToken;
public CustomUrl setUser(String userId) {
mUserId = userId;
return this;
}
public CustomUrl setToken(String token) {
mToken = token;
return this;
}
}
The values are not necessary for the #Key annotations, but will be used as the name of the respective query parameters if provided. If omitted, the name of the variable will be used instead (see example)
Check google-http-client's javadoc for more info.

Post/Put/Delete http Json with additional parameters in Jersey + general design issues

For some reason, I haven't found any normal way to do the following:
I want to Post a json object, and add additional parameters to the call (in this case, an authentication token).
This is a simple RESTful server in myUrl/server, which should give access to different resources of a "person" in the url myUrl/server/person/personCode/resourceName.
GET is easy, and requires no object, only parameters.
The problem arrises when I get to POST - how do I attach the JSON, and keep the other parameters as well?
The class (much has been removed for clarity and proprietary reasons...):
//Handles the person's resources
#Path("/person/{personCode}/{resourceName}")
public class PersonResourceProvider {
#GET
#Produces("application/json")
public String getPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Authenticates the user in some way, throwing an exception when needed...
authenticate(personCode, auth_token, resourceName);
//Returns the resource somehow...
}
#POST
#Produces("application/json")
public String postPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("resourceData") String resourceData, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Again, authenticating
authenticate(personCode, auth_token, resourceName);
//Post the given resource
}
}
Now, the GET method works perfectly, when you go to
myUrl/person/personCode/resourceName, it gives me the correct resource.
The auth_token is used with every single call to the server (for now, authentication is done by comparing with a predefined string), so it's needed. All the other parameters are provided through the path, except for the authentication token, which should not be in the path as it does not relate to the identity of the required resource.
When I get to POST, it's a problem.
I know there's a way to tell the method it consumes a JSON, but in that case, what will happen to the other parameters (auth_token is one of them)?
Should I use Multipart?
Another related question, this is the first time I've designed such a server, is this design correct?
Thanks!
I am not sure I understand what you are trying to achieve. Let me try explain a few things - hope it will be relevant to your question:
#QueryParam injects parameters which are part of your path - i.e. the part of the URL that goes after "?".
E.g. if you have a URL like this:
http://yourserver.com/person/personCode/resourceName?resourceData=abc&token=1234
Then there would be 2 query params - one named resourceData with value "abc" and the other one named token with value "1234".
If you are passing an entity in the POST request, and that entity is of application/json type, you can simply annotate your post method using #Consumes("application/json") annotation and add another parameter to your method, which does not need to be annotated at all.
That parameter can be either a String (in that case Jersey would pass a raw JSON string and you would have to parse it yourself) or it can be a java bean annotated with #XmlRootElement annotation - in that case (if you also include jersey-json module on your classpath) Jersey will try to unmarshall the json string into that object using JAXB. You can also use Jackson or Jettison libraries to do that - see this section of Jersey User Guide for more info: http://jersey.java.net/nonav/documentation/latest/json.html
Found!
Client side:
Client c = Client.create();
WebResource service = c.resource("www.yourserver.com/");
String s = service.path("test/personCode/resourceName")
.queryParam("auth_token", "auth")
.type("text/plain")
.post(String.class, jsonString);
Server side:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
#Path("/test/{personCode}/{resourceName}")
public class TestResourceProvider {
#POST
#Consumes("text/plain")
#Produces("application/json")
public String postUserResource(String jsonString,
#PathParam("personCode") String personCode,
#PathParam("resourceName") String resourceName,
#QueryParam("auth_token") String auth_token)
throws UnhandledResourceException {
//Do whatever...
}
}
In my case, I will parse the json I get in the server depending on the resource name, but you can also pass the object itself, and make the server consume an "application/json".