The Head Rush
The Head Rush

Reputation: 3357

How Can I Generate a POJO from an HTTP POST Request in a Spring Boot Application

I have an Angular 2 service that POSTs login data to a Spring Boot @RestContoller

The login request body:

{"_username": "test", "_password": "test"}

The Rest Controller:

@RestController
public class AuthControl {
    @Autowired
    AuthenticationService authenticationService;

    @RequestMapping(value = "/someurl", method = RequestMethod.POST)
    public LoginDto authenticate(HttpServletResponse response, LoginRequestDto requestDto) throws IOException, JSONException {
        return authenticationService.authenticate(requestDto, response);
    }
}

Jackson unmarshalls the request into this POJO

public class LoginRequestDto {
    private String _username;
    private String _password;

    public LoginRequestDto() { }
    public LoginRequestDto(String u, String p) {
        this._username = u;
        this._password = p;
    }

    // getters and setters
}

However, when Jackson unmarshalls the POJO the field values are set to null, rather than "test"/"test". As a result, the login attempt fails with a bad credentials message. I'm wondering if i'm missing a library or something that is resulting in the improper setting of these fields.

Dependencies from pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency><!-- implements Thymeleaf HTML5 parsing -->
        <groupId>net.sourceforge.nekohtml</groupId>
        <artifactId>nekohtml</artifactId>
        <version>1.9.22</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-json-org</artifactId>
        <version>2.4.0</version>
    </dependency>
</dependencies>

Tthe only exception logged is a UsernameNotFoundException thrown by my implmentation of Spring Security's UserDetailsService. Using the debugger, I can manually parse the values from the request and set the values into the POJO. After doing this, the request completes, so it seems clear that the issue is in the unmarshalling phase.

Any thoughts or suggestions would be greatly appreciated.

Upvotes: 0

Views: 2601

Answers (1)

alfcope
alfcope

Reputation: 2427

That is due to Jackson using the default constructor, by default, when deserializing. You need to use the @JsonCreator annotation to let jackson know which the constructor you want to use is and the the annotation @JsonProperty to let it know how to bind the params with the json properties.

public class LoginRequestDto {
    private String _username;
    private String _password;

    public LoginRequestDto() { }

    @JsonCreator
    public LoginRequestDto(@JsonProperty("_username") String u,
                           @JsonProperty("_password") String p) {
        this._username = u;
        this._password = p;
    }

    // getters and setters
}

Have a look to the section Using constructors or factory methods at the jackson-annotations project.


Update your request mapping method specifying it consumes json and add the @RequestBody annotation to the method param

@RestController
public class AuthControl {
    @Autowired
    AuthenticationService authenticationService;

    @RequestMapping(value = "/someurl", method = RequestMethod.POST, consumes = { MediaType.APPLICATION_JSON_VALUE })
    public LoginDto authenticate(HttpServletResponse response, @RequestBody LoginRequestDto requestDto) throws IOException, JSONException {
        return authenticationService.authenticate(requestDto, response);
    }
}

Upvotes: 1

Related Questions