7

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!

Carloshf
  • 529
  • 1
  • 6
  • 25
  • What is the error getting from the server, I meant the stacktrace is something like MessageConverter issue ? – Koitoer Mar 03 '16 at 17:14

5 Answers5

6

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.

Carloshf
  • 529
  • 1
  • 6
  • 25
  • Thank you very much! Helped me by adding default constructor for a field class inside exposed dto – Yauhen Sep 17 '18 at 15:09
1

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);
       });
Vikrant Kashyap
  • 6,398
  • 3
  • 32
  • 52
  • Thanks for the tip, but apparently it does the same. I have the same problem when trying to delete an user with the method DELETE instead of POST, actually I have something very similar to your solution, but I just get the same error 400... And the console does not log anything useful, just the html with the error 400... I guess I will just send the values I want to update as parameters, because this is taking such a lot of time... – Carloshf Mar 03 '16 at 18:27
0

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.

jstuartmilne
  • 4,398
  • 1
  • 20
  • 30
  • how can I enable the DEBUG on wildfly? had never done it before... But seeing the error probably would be helpful. – Carloshf Mar 03 '16 at 17:30
  • are you using spring-boot? – jstuartmilne Mar 03 '16 at 17:32
  • I dont know what is that, I am only using eclipse with a wildfly server. – Carloshf Mar 03 '16 at 17:35
  • Take a look at this http://www.mkyong.com/spring-mvc/spring-mvc-log4j-integration-example/. However to resolve your above problem you can try manually Serializing/Deserializing your object. Just to check that works as expected – jstuartmilne Mar 03 '16 at 17:39
  • But the thing is that our services are working perfectly for every class, I don't want to put another layer to deserialize the object... The error must be something silly for sure... We have complex classes with tons of references and on this very simple class with 6 attributes, just does not work :( – Carloshf Mar 03 '16 at 17:43
  • No layer, Just for testing purpeses. I mean like JUnit. a class just to test if everything is working as expected. You can even errase it afterwords :P. Or even a TEST main method with a sysout. I just want to know if its a jackson problem – jstuartmilne Mar 03 '16 at 17:54
0

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

Community
  • 1
  • 1
Nesta
  • 21
  • 4
  • Sorry for my ignorance but I am not so experience on Spring to add Debug, I read some documentation but I have no clue, no application.properties, no log4j file or anything like that. And about the thing with the enum, that is not the problem, because with KeyProfileUser it works and the enum is sent the same way. I will keep searching for a solution tomorrow, but thanks for the tip, it would be very useful to see something else on the log. – Carloshf Mar 03 '16 at 18:54
  • what helped me getting my controller to working after having the same problem, was simply adding a get method and see what the controller "thinks" is a valid object. This way i found by missing "s" for the property question**s** ;) – Nesta Mar 03 '16 at 19:21
  • You can get the exception inside without setting log level to DEBUG if you are able to set a breakpoint inside the DispatcherServlet. For me (spring 3.2.9) it's DispatcherServlet ``private void processDispatchResult`` line number 993. Just search for a "logger.isDebugEnabled()" inside the dispatcher servlet. – Nesta Mar 04 '16 at 09:35
  • I found the solution!! Setting debugger to DEBUG did not show anything different (maybe I did something wrong) but setting there a breakpoint let me see the problem! Thanks a lot, I really spent lot of time to find this out! – Carloshf Mar 04 '16 at 10:19
0

I had a similar problem since our project was set up to read json in snake case instead of camel case

brando f
  • 311
  • 2
  • 9