2

I was just playing with the spring-boot, just wanted to create a controller with method = RequestMethod.POST

my controller:

@RequestMapping(value = "/user/signup",
        method = RequestMethod.POST)
private String signUpNewUser(@Valid @RequestBody SignInUserForm userForm){
        // validation + logic
}

SignUpUserForm class:

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class SignInUserForm {
    @NotEmpty
    @Email
    private String email;

    @NotEmpty
    @Size(min = 8, max = 24)
    private String password;

    @NotEmpty
    @Size(min = 8, max = 24)
    private String repeatedPassword;
}

and finally my test:

@Test
    public void shouldCallPostMethod() throws Exception {

        SignInUserForm signInUserForm = new SignInUserForm("test@mail.com", "aaaaaaaa", "aaaaaaaa");

        String json = new Gson().toJson(signInUserForm);

        mockMvc.perform(
                MockMvcRequestBuilders.post("/user/signup")
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(json))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isCreated());
    }

as far as the SignUpControllerForm contain no-args constructor, everything works fine, but once it is missing this is what i receive from the MockMvcResultHandlers.print():

MockHttpServletRequest:
         HTTP Method = POST
         Request URI = /user/signup
          Parameters = {}
             Headers = {Content-Type=[application/json]}

             Handler:
                Type = org.bitbucket.akfaz.gui.controller.SignUpController
              Method = private java.lang.String org.bitbucket.akfaz.gui.controller.SignUpController.signUpNewUser(org.bitbucket.akfaz.gui.model.SignInUserForm)

               Async:
       Async started = false
        Async result = null

  Resolved Exception:
                Type = org.springframework.http.converter.HttpMessageNotReadableException

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:

MockHttpServletResponse:
              Status = 400
       Error message = null
             Headers = {}
        Content type = null
                Body = 
       Forwarded URL = null
      Redirected URL = null
             Cookies = []

What I want to express is that, the exception HttpMessageNotReadableException is not descriptive enough. Shouldn't be there any exception connected with the @RequestBody? It would save a lot of time.

And how exactly Spring converts JSON to java object with no-arg constructor (it does not uses the getters as i checked)?

pezetem
  • 2,503
  • 2
  • 20
  • 38
  • Enable debug logs. Spring prints the exception message there. – Sotirios Delimanolis Oct 06 '15 at 17:29
  • All serializable classes require a no-args constructor. This is a fundamental rule of java, not a Spring limitation per se. (Jackson tolerating you not marking the class serializable not withstanding :) ) – Affe Oct 06 '15 at 17:48

1 Answers1

6

As @Sotitios said you can enable debug logs you can do this by adding a logback.xml (it can be groovy too) to your resources folder. here is mine

<configuration>
    <appender name="FILE"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>logFile.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>logFile.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>5</maxHistory>
        </rollingPolicy>

        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} -
                %msg%n</Pattern>
        </layout>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>

</configuration>

Regarding you question on no-arg constructors you can create your own constructor and force jackson to use it, I believe this is a good practice since you have more control on mutablity

@JsonCreator
    public UserDto(
            @JsonProperty("id") Long id, 
            @JsonProperty("firstName") String firstName, 
            @JsonProperty("lastName") String lastName,
            @JsonProperty("emailAddress") String emailAddress,
            @JsonProperty("active") boolean active,
            @JsonProperty("position") String position,
            @JsonProperty("pendingDays") Integer pendingDays,
            @JsonProperty("taxUid")  String taxUid,
            @JsonProperty("userName") String userName,
            @JsonProperty("approver") boolean approver,
            @JsonProperty("startWorkingDate") Date startWorkingDate,
            @JsonProperty("birthDate") Date birthDate){

        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.taxUid = taxUid;
        this.userName = userName;
        this.emailAddress = emailAddress;
        this.pendingDays = pendingDays;
        this.position = position;
        this.active = active;
        //this.resourceUrl = resourceUrl;
        this.isApprover = approver;
        this.birthDate = birthDate;
        this.startWorkingDate = startWorkingDate;

    }

Hope this helps

jstuartmilne
  • 4,398
  • 1
  • 20
  • 30
  • Since java 8 you can get rid of the annotations @JsonProperty as long as you keep the same names between the class and the JSON. For that you need to add the jackson-module-parameter-names dependency (included in latest versions of spring-boot). – Alexi Courieux Jul 03 '22 at 15:58