When using Spring, what is considered best practice when both JSON and XML is to be returned?
Say I have a method:
#RequestMapping(value="items/{id}", method = RequestMethod.GET)
public Item getItem(#PathVariable, Long id) {
// Find and return item.
}
How can I invoke this in a style like:
localhost:8080/app-name/items/1.xml and localhost:8080/app-name/items/1.json and get the data in their respective formats?
Most obvious solution:
#RequestMapping(value="items/{id}.{format}", method = RequestMethod.GET)
public Item getItem(#PathVariable, Long id, String format) {
if(format.equals("xml"))
...
}
This is discussed in the Spring documentation here.
You can use two separate URLs to determine the type to return
You can use accept headers. However from a browser you are unable to set the accept headers. Therefore using separate URL's gives more control.
Use #ResponseBody and ensure that you have HttpMessageConverters registered with your HandlerAdapter that can handle your object Item with the JSON and XML mime types. This also allows you to keep from duplicating controllers that return the same Item and keeps your code simpler to maintain and test.
See this for additional info:
http://www.ibm.com/developerworks/web/library/wa-restful/
Related
So I made Rest Client using Spring Boot that is consuming a Rest Web Service. I am passing the required requestbody but on printing the requestbody out it is not the same as my input.
For Example: What I entered was TransactionId then it would be changed to transactionId, AB_NAME would be changed to ab_NAME.
So all these fields get assigned null values.
The ResponseEntity being formed also does the same thing. I don't know why this is happening.
The dtos I have made are in line with the input I want to send so I don't know how they are changing on their own.
EDIT: So basically the web service dto fields are not using the Java naming convention but JSON automatically assumes them to be, had to use #JsonProperty to make sure the fields remain the same. Thanks for all of your help.
See your request body properties names and your model properties name should be the same as case. Writing here one example
DTO
publi class SomeName{
private string transactionId;
private string ab_NAME;
}
Sending body format should be
{
"transactionId":"11111",
"ab_NAME":"ABCDE"
}
Thanks.
I got a list that return from below db call.
List<employee> list = empolyeeRepository.findByEmployeeId(id);
List contains employee pojo class object. I want to remove one attribute let's say "employee bank account no" when returning from rest call.
#RequestMapping(value = "/employeeInformation/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public List<Employee> getEmployeeInformation(#PathVariable("id") String id) throws Exception {
return empolyeeRepository.findByEmployeeId(id);
}
Is there any annotation or good practice to do that?
As it mentioned in comments above, you cant remove fields of compiled class at runtime. Assuming you have to exclude some field from generated json, there I see two options:
Create a class with fields you want to be present in resulting json, copy required values from original object to a new created. This approach is called view model and allows you to decorate some object's data, hiding sensitive data from being exposed.
Depending on implementation of your serializer there may be annotations to exclude fields. #JsonIgnore may be placed on getter method, if you are using Jackson (default in spring boot). Second aproach requires significant less code, but the first one is more flexible.
Try #JsonIgnore to ignore properties from serialization and de-serialization.
Here is the link to the docs
my motive is to write one generic save method in REST API. User will send the entity in Request body such that depending upon Request Mapping String it should be converted to entity.
Why i am want this, because in my case there are as many as 50-60 entities and as per my understanding i have to write many controllers.
I am trying to achieve something like this.
#RequestMapping(value = "/{entity}", method = RequestMethod.POST)
#ResponseBody
public Object performSave(#PathVariable String entity
#RequestBody Object entity) {
switch(entity){
case "employee"
return employeeService.save((Employee)entity);
case "Boss"
return bossService.save((Boss)entity);
default:
return null;
}
but i am not able to do that because Spring cannot convert the JSON Request into java.lang.Object.
what possible solutions i have ?
If my question do not make sense to you, please let me know ,i will provide additional details.
Thanks in advance.
I don't think that is possible as the underlying mapper would need the concrete class which the json is parsed to. The parameter is simply a reference to the actual object.
Something to take note of is that when using REST and getting the benefit from it is not only having simple urls to call. One has to design APIs to be RESTfull. I would advice you to read up on that concept before going down this path you are heading.
It can be done with only one controller.
One possible implementation would be using JsonSubTypes and Java inheritance.
It is done by moddeling request body objects (entities, in the original question) that extend an abstract class. Request body parameter in the controller's method then has the type of the abstract class.
I have a controller with a method signature like this
#RequestMapping(value = "/{project:[A-Z0-9_+\\.\\(\\)=\\-]+}", method = RequestMethod.GET)
public ResponseEntity<Object> lookupProject(#PathVariable String project,
#RequestParam(value = "fields", required = false) String fields,
#RequestParam(value = "asList", required = false, defaultValue = "false") boolean asList);
I am returning for JSON a Collection<Object> or Map<String, Object>. This isn't suitable for XML. I'd rather return a Project object.
Is there a clean way to determine which content type will be produced? The work-around would be another method which produces XML only and returns the Project object. I'd rather like to avoid duplicate code.
I am on Spring 3.1.3-RELEASE.
According to Spring documentation, your produces param will return content based on the Accept, i.e.
#RequestMapping(value="/someUrl/", produces="application/*")
public Project someControllerMethod(...)
If the method takes in application/xml, the method will produce XML, if application/json, it will produce json, etc.
You just need marshallers set up for the appropriate object types.
Documentation here:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#produces()
Content type to be produced will depend on type acceptable by the client.One solution for this is already mentioned, using produces attribute.Another solution is using ContentNegotiatingViewResolver, which can resolve views based on accept header or other mechanism.
Refer the documentation http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.html
Example:http://www.mkyong.com/spring-mvc/spring-3-mvc-contentnegotiatingviewresolver-example/
Albeit, I think Dardo's solution is best, you could inspect the object type with your XMLHttpMessageConverter, and cast differently if it's a Project object.
In Spring Framework 4.1 you can use a ResponseBodyAdvice to modify the value returned from an #ResponseBody or ResponseEntity method just before it is written out.
See http://docs.spring.io/spring-framework/docs/4.1.0.RC2/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyAdvice.html.
I ended up in splitting in two methods.
Public methods have been renamed to lookupAsType. Both delegate/return
String[] fieldsArray = StringUtils.split(fields, ',');
return lookup(project, fieldsArray, asList, mediaType);
The new method lookup has the same code s before but contains not an if clause for the media types.
if (mediaType.equals(MediaType.APPLICATION_JSON)) {
body = projectValues;
} else if (mediaType.equals(MediaType.APPLICATION_XML)) {
body = new Project(projectValues);
} else {
throw new NotImplementedException("Project lookup is not implemented for media type '" + mediaType + "'");
}
The bad thing is that MediaType is not a enum but an ugly class.
Guys, Well I have done enough research still I can't find the solution to this.
In a nutshell, I'm simply passing url encoded form data to the Controller method and trying to convert it as a domain object which has Date and integers.
#RequestMapping(value = "/savePassport", method = RequestMethod.POST)
public #ResponseBody
AjaxResponse savePassport(#RequestBody StaffPassport passport, HttpServletResponse response) {
// Some operations.
}
The Staff Passport looks like this:
import java.sql.Date;
public class StaffPassport {
private int staffId;
private String passportNumber;
private String placeOfIssue;
private Date issueDate;
private Date expiryDate;
private String spouseName;
private String oldPassportRef;
private String visaInfo;
private String description;
//gets/sets
}
When I invoke the /savePassport, I get unsupported media exception. I guess it's related to casting.
I can't this working right. Of course I can catch individual form data using #RequestParam and manually do the casting but that's not the point of a framework isn't it?
Where am I going wrong? And you are right. I'm a beginner in Spring, but I love it.
Looks like you're using the wrong annotation. #RequestBody is for taking a request that has arbitrary content in its body,such as JSON, some application defined XML, comma separated variables.. whatever. And using a marshaller that you configure in the dispatcher servlet to turn it into objects.
If all you want to do is ask Spring to bind a plain old form post onto the backing object for you, the correct annotation to put on the method parameter is #ModelAttribute.
If you are posting a JSON Object with jQuery and you want Spring to be able to process it with #RequestBody, use JSON.stringify(....) in your data. Here an example:
var data = { "id": 3, "name": "test" }
$.post("processJsonData.html",JSON.stringify(data), function(data){
...
}
);
If you don't use the JSON.stringify() then you will submit the data as form data and Spring will tell you that you have an unsupported media type.
First of all be sure that you have
<mvc:annotation-driven />
in your Spring configuration file. This is mandatory for working with JSOn in SPring MVC.
Second, I recommend you to test wether request to the server has application/json content type. I belive Fiddler2 will help you to do so.
Third, but I'm not sure about it, Try to change Date items in your POJO from SQL type to regular java type.
UPDATE:
just looked at the Form and it seems like your "Accept" HTTP Header should be also application/json. Please test this issue with Fiddler2 as well.
I assume that you are posting JSON and want Spring to convert to StaffPassport. If you are getting an Unsupported media exception, it is because Spring could not figure out an appropriate way to perform the conversion.
For Spring to convert JSON, it needs Jackson -- make sure you have the Jackson jars in your project. If this is a Maven based project you can add the jackson-mapper-asl artifact ID to your pom.xml. This should give you the jackson-mapper and jackson-core jars.
Edit: I should mention that this applies to Spring 3 (I recently ran into this problem). I'm not sure what else is required for previous versions of Spring.
Check into HttpMessageConverter interface and its implementations. You could write your own implementation of it to convert it to the domain model you want. By the time the control gets to your method, you can access it as if your domain model object is passed.
Ok, I think I should refine my answer. I do not have direct experience of using it in a spring-mvc project but spring-integration. I am pretty sure the applicable media type (application/x-url-form-encoded) is already handled and converted to MultiMap by Spring framework; so, retrieve the values from that just like any other map with the key value being your form variable and populate your business model.
HTH.