3

I have a controller class with a few methods one of which is a method that is supposed to take POST requests and create a new Account with the JSON from the body of that POST request.

When I try to use curl to make the POST request I get an error that says

{"timestamp":1493988808871,"status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Required request body is missing: org.springframework.http.ResponseEntity<?> com.example.AccountRestController.add(java.lang.String,java.lang.String)","path":"/users/add"}

The curl command I'm using

curl -X POST --data '{"userName":"bepis", "password":"xyz"}' -H "Content-Type:application/json" http://localhost:8080/users/add

AccountRestController

@RequestMapping(method = RequestMethod.POST, value = "/add", produces = { MediaType.APPLICATION_JSON_VALUE})
ResponseEntity<?> add(@RequestBody String username, @RequestBody String password) {
    Account result = accountRepository.save(new Account (username, password));
    return new ResponseEntity<>(result, HttpStatus.CREATED);
}
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • What if you change to `ResponseEntity> add(@RequestParam("userName") String userName, @RequestParam("password") String password)` ? – StanislavL May 05 '17 at 13:07

2 Answers2

7

You cannot use multiple @RequestBody. You need to wrap everything into a class that will be used to match your request body.

The same is answered also here.

There is also a JIRA issue for a feature request which was rejected.

NOTE: if you want to write less, you can use @PostMapping instead of @RequestMapping(method = RequestMethod.POST).

NOTE: @RequestParam and @PathVariable are used to extract data from URI, not from body.

NOTE: the same is valid also for the equivalent [FromBody] attribute from ASP.NET WebAPI.

Complete example:

Bellow I created a working example similar to your case:

Request DTO

public class AccountCreateRequest {

    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Response DTO

public class AccountCreateResponse {

    private String userName;
    private String password;

    public AccountCreateResponse() {
    }

    public AccountCreateResponse(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Controller

@RestController
@RequestMapping("/v1/account")
public class AccountController {

    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseStatus(HttpStatus.CREATED) AccountCreateResponse add(@RequestBody() AccountCreateRequest account) {
        AccountCreateResponse response = new AccountCreateResponse(account.getUserName(), account.getPassword());
        return response;
    }
}

curl request

curl -X POST --data '{"userName":"bepis", "password":"xyz"}' -H "Content-Type:application/json" http://localhost:8080/v1/account
buræquete
  • 14,226
  • 4
  • 44
  • 89
andreim
  • 3,365
  • 23
  • 21
  • Thanks that let me make the POST request but the variables for userName and password are always null. I changed the method parameters to **@RequestBody Account input** – Lightning Jimmy Joe Johnson May 05 '17 at 17:08
  • @LightningJimmyJoeJohnson take a look at my last edit. I provided a working example for you similar to what you need. – andreim May 05 '17 at 19:53
  • Why have two classes different classes? Why not use **AccountCreateResponse** for both they seam functionally identical. – Lightning Jimmy Joe Johnson May 06 '17 at 04:03
  • Is just an example to point out request and response objects. You can use as you like. Usually I create separate DTOs for request and response because can handle complex business scenarios and most of the time, in my case, I encountered these two to be different. This introduce complexity but more flexibility. You can use the same class if works for your case. – andreim May 06 '17 at 04:39
0

So I changed @Requestbodys to a single @RequestBody like andreim said, it fixed my bad request error but for some reason the account username would always be null when the post request went through.

I messed with a few thing, eventually leading me to swap the order of the Account constructor parameters from

Account(String username, String password)

to

Account(String password, String username)

This worked for some reason and let me make my post request with the proper values. Latter out of curiosity I decided to swap the parameters back to Account(String username, String password) and the post request still works as intended.

I have no idea what I did to get rid of the null problem but it seams to have worked.

  • This is a very old post so maybe I a commenting for no reason but your Controller expects username with `{"username":......}`. Note the cases of username. Earlier you sent `userName`. The capital N was causing mapping issues. – Drishti Jain Nov 24 '22 at 09:30