#RequestBody not working on Rest service - java

I am developing a web application with AngularJS and WildFly using Spring also.
My problem is that I am going nuts because the annotation #requestBody appears to work wrong.
This is my service:
#ResponseBody
#RequestMapping(value = "/keyuser", method = RequestMethod.POST,
consumes = "application/json")
public KeyProfileUserSummary updateEmployee(#RequestBody KeyProfileUserSummary keyUser) {
return null;
}
And this is are the members of my object KeyProfileUserSummary:
private Integer id;
private String login;
private String password;
private String firstname;
private String lastname;
private UserRole userRole;
I don't know what is going on but I have tested this service with other types of objects and it works perfectly, but when defining KeyProfileUserSummary it is not working, I get ERROR 400 BAD REQUEST. I have tested to set the #RequestBody to "Object" so at least I can see what is coming, and from my front end, I am getting the following:
{id=3, login=aa, password=a, firstname=Martin, lastname=Müller, userRole=ROLE_USER}
UserRole is an Enum. Important to clearify that KeyProfileUserSummary is just a summary version of KeyProfileUser, but due to all the linked elements I get on the response, I decided to send this lighter class. Testing with KeyProfileUser worked perfectly, I get the JSON object on the Angular side and can send it back.
On the Angular side, I am not doing anything with the object. Just receive it on a list, and when pressing an edit button just send the element on the list back. This is the way I am sending it:
res = $http.post("url.../keyuser", user);
The thing is that I had everything working perfectly with KeyProfileUser, but as the database can get really huge and the reference are quite a lot, I decided to switch to this lighter class, but now I only get this ERROR 400 BAD REQUEST... And I am about to hang myself :P
Thanks for your help!

Ok so finally I found the solution.
In my KeyProfileUserSummary I only had one constructor that was taking a KeyProfileUser and set the attributes to the summary version:
public KeyProfileUserSummary(KeyProfileUser keyProfileUser) {
this.id = keyProfileUser.getId();
this.login = keyProfileUser.getLogin();
this.password = keyProfileUser.getPassword();
this.firstname = keyProfileUser.getPerson().getFirstname();
this.lastname = keyProfileUser.getPerson().getLastname();
this.userRole = keyProfileUser.getUserRole();
}
And apparently, setting a breakpoint in line 993 of the dispatchler servlet (thanks to #Clemens Eberwein for the tip) I realised that when parsing from a JSON object, the Jackson parser needs an empty constructor! So adding it solved it and works perfectly.
Note: for KeyProfileUser, it was working perfectly as we had the annotation #Entity for hibernate, and therefore the empty constructor was automatically created.

Try this out.. might be useful for you..
$http({
method: 'POST',
url: 'http://localhost:8080/keyuser',
data: user,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}}).then(function(result) {
console.log(result);
}, function(error) {
console.log(error);
});

If I were to guess, jackson is failing in deserializing/serializing your object. Here is an util I made:
import java.io.IOException;
import java.nio.charset.Charset;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class SerializeDeserializeUtil {
public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(
MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
public static byte[] convertObjectToJsonBytes(Object object)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
public static <T> T deserializeObject(String jsonRepresentation,
Class<T> clazz) throws JsonParseException, JsonMappingException,
IOException {
ObjectMapper mapper = new ObjectMapper();
Object obj = mapper.readValue(jsonRepresentation.getBytes(), clazz);
return clazz.cast(obj);
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public static byte[] convertObjectToJsonBytesWithCustomSerializer(
Object object, JsonSerializer serializer, Class clazz)
throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule sm = new SimpleModule();
sm.addSerializer(clazz, serializer);
mapper.registerModule(sm);
mapper.setSerializationInclusion(Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
}
Try creating a test just to serialize and deserialize the objec. create a KeyProfileUserSummary object and try deserializing/serializing too see if jackson complains.
A more easier way is to enable DEBUG logging and checking the log file, by default you don't get to see this kind of errors
Hope it helps.

If you add DEBUG logging for "org.springframework.web" org.springframework.web.servlet.DispatcherServlet should give you detailed information what causes the "400 Bad Request" error.
Details about Wildfly logging configuration can be found here
Based on your KeyProfileUserSummary class i guess the problem is the UserRole object which ist just userRole=ROLE_USER in the example above. As it is an object it should be enclosd by curly braces and the property name must be set. e.g. something like
userRole = { name = "ROLE_USER"}
If it is an enum, this answer might be helpful

Related

Merge json payload values to Object

I want to merge the REST PATH payload to the 'Entity' object after getting it from a database, so that only the attributes provided in payload will be updated in entity. Hence, I want to ensure that only the attributes provided as part of patch payload will be updated safely.
I am using Spring Rest Controller with Hibernate entities.
#PatchMapping(value = "/{id}")
public Resource<DepartmentPEO> update(#PathVariable Long id,
#RequestBody JSONObject payload) throws Exception
{
DepartmentPEO eo = departmentService.getRow(id);
// Have to do something to update the eo object from jsonObject.
// Some api to update eo
eo = departmentService.update(id, eo);
Resource<DepartmentPEO> resource = new Resource<>(eo);
DepartmentPEO dept = resource.getContent();
id = dept.getDeptSeq();
resource.add(
linkTo(methodOn(DepartmentsRestController.class).getRow(id))
.withSelfRel());
return resource;
}
Only the modified attributes will be sent as part of payload to server instead of sending all attributes.Resource(entity) will have nested list of objects (One-to-many). Am looking for the pool-proof solution for this use case and also believe this is common/basic for every rest api supported apps.
Pointing to any API to solve this would greatly appreciated!
Thank you
Here is a working example using Jackson's ObjectMapper and BeanUtils from Spring (since I assume you're using Spring) :
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
public class StackOverflow {
#Test
public void mergeTest() throws IOException, JSONException {
DepartmentPEO existingDepartement = existingDepartmentPEO();
JSONObject payload = new JSONObject();
payload.put("name", "newName");
DepartmentPEO result = mergeToObject(payload, existingDepartement);
assert result.getName().equals("newName");
assert result.getId().equals("1");
}
private DepartmentPEO existingDepartmentPEO() {
DepartmentPEO existingDepartement = new DepartmentPEO();
existingDepartement.setId("1");
existingDepartement.setName("oldName");
return existingDepartement;
}
private DepartmentPEO mergeToObject(JSONObject payload, DepartmentPEO object) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
DepartmentPEO updateRequest = objectMapper.readValue(payload.toString(), DepartmentPEO.class);
BeanUtils.copyProperties(updateRequest, object, "id");
return object;
}
}
Here, I transform the JSONObject into a DepartmentPEO class then I copy this object into the existing one ignoring the field id.
You may want to have a generic way to ignore null fields from the JSONObject, then you can refer to this post for instance How to ignore null values using springframework BeanUtils copyProperties?
I would advice to send directly the DepartmentPEO object into the REST method signature instead of using a JSONObject.
Regards

JAX-RS (Reasteasy) Response.readEntity throws: IllegalStateException: RESTEASY003290: Entity is not backed by an input stream

I have JUnit test of a method which send a JAX-RS POST call.
To be independent from external resources I have mocked the REST client and said that a dummy response should be returned. Works great, no problem. But:
When calling myResponse.readEntity(String.class) I always get the following Exception:
java.lang.IllegalStateException: RESTEASY003290: Entity is not backed by an input stream
Here is my code snippet which fails:
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.Test;
public class SimpleTest {
#Test
public void testReadResponse() {
final JsonObject responseContent = new JsonObject();
responseContent.add("field", new JsonPrimitive("This is a JSON for testing."));
final String expected = responseContent.toString();
final Response.ResponseBuilder builder = Response.ok()
.entity(responseContent.toString())
.header("Content-Type", MediaType.APPLICATION_JSON);
final Response dummyResponse = builder.build();
final String result = dummyResponse.readEntity(String.class); // <-- Exception is thrown here!
assertThat("JSON Strings are not identical.", result, is(expected));
}
}
and the Stacktrace:
java.lang.IllegalStateException: RESTEASY003290: Entity is not backed by an input stream
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:230)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:219)
at de.me.myproject.SimpleTest.testReadResponse(SimpleTest.java:43)
In my production code, which calls a not mocked REST API, it returns a automatically build response, where the .readEntity(String.class) method works fine.
Response is an abstract class and RESTEasy has different sub classes for client and server, see BuiltResponse and ClientResponse. Not all methods are supported in each sub class.
Response#readEntity needs to be backed by an input stream:
Method throws an ProcessingException if the content of the message cannot be mapped to an entity of the requested type and IllegalStateException in case the entity is not backed by an input stream or if the original entity input stream has already been consumed without buffering the entity data prior consuming.
A BuiltResponse is never backed by an input stream and therefore you get a IllegalStateException.
You can use Response#getEntity, it doesn't need an input stream.
Thanks for the hint.
I ended up with the following that worked for me. Simply return a new instance of the class below.
class MockResponse extends BuiltResponse {
private Object entity;
public MockResponse() {
}
public MockResponse(Object entity) {
this.entity = entity;
}
#Override
public <T> T readEntity(Class<T> type) {
return (T) entity;
}
#Override
public <T> T readEntity(Class<T> type, Type genericType, Annotation[] anns) {
return (T) entity;
}
}
Today I went through the same situation. Finally I have the below solution. Just mock the readEntity method in BuiltResponse to return whatever response you need. It worked for me.

FAIL_ON_UNKOWN_PROPERTIES not working as expected on Jersey POST

Using this post as a reference I put together a bare bones Jersey controller method for POST calls that looks like this:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createVisit(Visit newVisit) {
LOGGER.info("Creating visit");
this.visits.add(newVisit);
return Response.ok(newVisit)
.build();
}
Here are the fields on my Visit object (constructors and getter/setters omitted because I don't think they're relevant here - I can add them in if they'd be helpful):
public class Visit {
private VisitId id;
private AthleteId athleteId;
private CustomerId customerId;
private StoreId storeId;
private Instant createdUtc;
private Instant lastModifiedUtc;
}
When I pass in valid Visit object fields in JSON format in the request body, I see the Visit object successfully populated in the response as expected. However, if I add fields that aren't part of the Visit object to the request body they seem to be ignored.
I've seen a number of posts trying to figure out how to disable the FAIL_ON_UNKNOWN_PROPERTIES property, but I seem to be having the opposite issue. My understanding is that FAIL_ON_UNKNOWN_PROPERTIES defaults to true, but in that case I would expect to get an error response code (500?) when the JSON object in the request body doesn't match with the object I'm passing in to my POST method. Any ideas on what I'm missing here?
Use a ContextResolver, as mentioned in the documentation
#Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public MyObjectMapperProvider() {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
}
Then you need to make sure the resolver is registered. If you are using some scanning mechanism to auto-register your resources and providers, this class should be picked up with the #Provider annotation

Custom response on bad request using spring RestController

I have the following controller. I am using Spring to create Restful APIs.
#RestController
public class UserController extends RestControlValidator {
#RequestMapping(value = "/user/", method = RequestMethod.POST, headers = "Accept=application/json", consumes = "application/json", produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List newUser(#RequestBody #Valid UserInput input,BindingResult result)
{Some code}
}
The UserInput class looks like this:
public class UserInput{
#NotEmpty
private String emailId;
#NotEmpty
private String fName;
private String lName;
private int sex;
//getters and setters
Now when I try and access /user/ with data {"sex":"Male"}, I get the following response:
I want the response in case of such a request to be:
{"errors":{"sex":"The value must be an integer"}}
Is there any way of customising BAD REQUEST responses in Spring?
Considering the current scenario the most ideal solution would be to alter the behavior of HandlerMethodArgumentResolve as the json to pojo constructed by #RequestBody fails because we dont get a chance to check the wrong data and this check can very well be done in the custom message converter
A. first we would need to create LanguageMessageConverter as follows
public class LanguageMessageConverter extends
AbstractHttpMessageConverter<Language> {
private Gson gson = new Gson();
public LanguageMessageConverter() {
super(new MediaType("application", "json", Charset.forName("UTF-8")));
}
#Override
protected boolean supports(Class<?> clazz) {
return Language.class.equals(clazz);
}
Map<String, String> mp = new HashMap<>();
#Override
protected Language readInternal(Class<? extends Language> clazz,
HttpInputMessage httpInputMessage) throws IOException,
HttpMessageNotReadableException {
Map langmp = gson.fromJson(
convertStreamToString(httpInputMessage.getBody()), Map.class);
for (Field field : clazz.getDeclaredFields()) {
if (!langmp.get(field.getName()).getClass().getCanonicalName().equals(field.getType().getCanonicalName())) {
if (field.getType().getCanonicalName().equals("java.lang.Integer")||field.getType().getCanonicalName().toString().equals("int")) {
langmp.put(field.getName(), "0");
} else if (field.getType().equals("java.lang.String")) {
//TODO COde needs to be improved here because this check is not efficient
langmp.put(field.getName(), "wrong");
}
}
}
Language lang = gson.fromJson(gson.toJson(langmp), clazz);
return lang;
}
we need to set the media type new MediaType("application", "json", Charset.forName("UTF-8")) which will make sure this class intervenes the mentioned MIME type
Considering we need to manipulate the result I found it best to convert it to map langmp (There are better JSON Parsers which can be used)
Since we need to to understand the existing type I used reflection api to get the fields via getDeclaredFields()
Using the above made the logical check using the datatype to understand if the type is incorrect for eg if the field datatype is int and if it is found as String then corresponding map value will be substituted
once that is done the map will hold the updated values where in if the data was wrong a default value would be set eg if the int var is set to 0 since the originating json had a String in it.
Once that is done the updated map is converted to the concerned class.
B. Secondly we need to register the custom MessageConverter in the dispatcher xml i.e. LanguageMessageConverter
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="com.comp.org.controller.LanguageMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
register-defaults="true" is very important since we are adding Custom MessageConverter but we also need the other existing converters working along with the one we have added
LanguageMessageConverter needs to be registered here.
C. Considering the concerned pojo is populated with the necessary details it would reach our controller post processing in the custom converter now we would add the manual validation eg. if the int variable has 0 the necessary error json should be returned
As per your request even if the json consists of the wrong data the custom message converter should process it and accordingly in the controller we can validate the condition mentioned.
The code definitely can be improved further. Kindly let me know if this solution fulfilled your requirement or any part of the code requires further elaboration and hopefully addressed your concern.
I had the same issue, than I solved that way:
Create an Object called Error, like that (don't forget to implement Serializable...):
private String fieldName;
private String errorCode;
private String defaultMessage;
public Error() {
}
public Error(String fieldName, String errorCode, String defaultMessage) {
this.fieldName = fieldName;
this.errorCode = errorCode;
this.defaultMessage = defaultMessage;
}
/* getters, setters */
Inside the #RestController method you ave to call inputValidator.validate() method (if you didn't create an Object Validator for your UserInput then we're really don't speaking the same language...)
// validating the userInput
userInputValidator.validate(userInput, bindingResult);
if (bindingResult.hasErrors()) {
List<Error> errors = new ArrayList<>(bindingResult.getErrorCount());
for (FieldError fieldWithError : bindingResult.getFieldErrors()) {
errors.add(new Error(fieldWithError.getField(), fieldWithError.getCode(), fieldWithError.getDefaultMessage()));
}
return errors;
}
// in case of success:
return null;
Finally you'll have to translate the JSON object to your client side. You'll have two kind of objects:
3.1. null (undefined depending on the language you're using)
3.2. A JSON object like that:
[
{
"fieldName": "name",
"errorCode": "user.input.name.in.blank",
"defaultMessage": "Insert a valid name!"
},
{
"fieldName": "firstPhone",
"errorCode": "user.input.first.phone.blank",
"defaultMessage": "Insert a valid first phone!"
}
]

JSON parameter in spring MVC controller

I have
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
SessionInfo register(UserProfile profileJson){
...
}
I pass profileJson this way:
http://server/url?profileJson={"email": "mymail#gmail.com"}
but my profileJson object has all null fields. What should I do to make spring parse my json?
The solution to this is so easy and simple it will practically make you laugh, but before I even get to it, let me first emphasize that no self-respecting Java developer would ever, and I mean EVER work with JSON without utilizing the Jackson high-performance JSON library.
Jackson is not only a work horse and a defacto JSON library for Java developers, but it also provides a whole suite of API calls that makes JSON integration with Java a piece of cake (you can download Jackson at http://jackson.codehaus.org/).
Now for the answer. Assuming that you have a UserProfile pojo that looks something like this:
public class UserProfile {
private String email;
// etc...
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// more getters and setters...
}
...then your Spring MVC method to convert a GET parameter name "profileJson" with JSON value of {"email": "mymail#gmail.com"} would look like this in your controller:
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper; // this is your lifesaver right here
//.. your controller class, blah blah blah
#RequestMapping(value="/register", method = RequestMethod.GET)
public SessionInfo register(#RequestParam("profileJson") String profileJson)
throws JsonMappingException, JsonParseException, IOException {
// now simply convert your JSON string into your UserProfile POJO
// using Jackson's ObjectMapper.readValue() method, whose first
// parameter your JSON parameter as String, and the second
// parameter is the POJO class.
UserProfile profile =
new ObjectMapper().readValue(profileJson, UserProfile.class);
System.out.println(profile.getEmail());
// rest of your code goes here.
}
Bam! You're done. I would encourage you to look through the bulk of Jackson API because, as I said, it is a lifesaver. For example, are you returning JSON from your controller at all? If so, all you need to do is include JSON in your lib, and return your POJO and Jackson will AUTOMATICALLY convert it into JSON. You can't get much easier than that. Cheers! :-)
This could be done with a custom editor, that converts the JSON into a UserProfile object:
public class UserProfileEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
ObjectMapper mapper = new ObjectMapper();
UserProfile value = null;
try {
value = new UserProfile();
JsonNode root = mapper.readTree(text);
value.setEmail(root.path("email").asText());
} catch (IOException e) {
// handle error
}
setValue(value);
}
}
This is for registering the editor in the controller class:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(UserProfile.class, new UserProfileEditor());
}
And this is how to use the editor, to unmarshall the JSONP parameter:
#RequestMapping(value = "/jsonp", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
SessionInfo register(#RequestParam("profileJson") UserProfile profileJson){
...
}
You can create your own Converter and let Spring use it automatically where appropriate:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
#Component
class JsonToUserProfileConverter implements Converter<String, UserProfile> {
private final ObjectMapper jsonMapper = new ObjectMapper();
public UserProfile convert(String source) {
return jsonMapper.readValue(source, UserProfile.class);
}
}
As you can see in the following controller method nothing special is needed:
#GetMapping
#ResponseBody
public SessionInfo register(#RequestParam UserProfile userProfile) {
...
}
Spring picks up the converter automatically if you're using component scanning and annotate the converter class with #Component.
Learn more about Spring Converter and type conversions in Spring MVC.
This does solve my immediate issue, but I'm still curious as to how you might pass in multiple JSON objects via an AJAX call.
The best way to do this is to have a wrapper object that contains the two (or multiple) objects you want to pass. You then construct your JSON object as an array of the two objects i.e.
[
{
"name" : "object1",
"prop1" : "foo",
"prop2" : "bar"
},
{
"name" : "object2",
"prop1" : "hello",
"prop2" : "world"
}
]
Then in your controller method you recieve the request body as a single object and extract the two contained objects. i.e:
#RequestMapping(value="/handlePost", method = RequestMethod.POST, consumes = { "application/json" })
public void doPost(#RequestBody WrapperObject wrapperObj) {
Object obj1 = wrapperObj.getObj1;
Object obj2 = wrapperObj.getObj2;
//Do what you want with the objects...
}
The wrapper object would look something like...
public class WrapperObject {
private Object obj1;
private Object obj2;
public Object getObj1() {
return obj1;
}
public void setObj1(Object obj1) {
this.obj1 = obj1;
}
public Object getObj2() {
return obj2;
}
public void setObj2(Object obj2) {
this.obj2 = obj2;
}
}
Just add #RequestBody annotation before this param

Categories

Resources