1

I have this old endpoint that I am trying to upgrade to using spring boot. It is currently used by a few services so I would like to keep it the same.

The old endpoint accepts a Request Body of userName=1234&password=1234.

As an example of how it is called is curl -X POST "http://localhost:8080/AuthService/login" -H "accept: */*" -H "Content-Type: application/json" -d "userName=123&password=123"

How can I get spring to take the Request Body exactly like my old endpoint but map the input into an Object for me?

The old endpoint was using Apache Access and is like this. It was configured to transform any public method in the class into an endpoint.

public RequestStatus login(String userName, String password) {
    ...
}

I have translated it over to Spring Boot like this

@ApiOperation(value = "To login", response = RequestStatus.class)
@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<ReturnValue> login(@RequestBody() String info) {
    Login login = new Login(info);
    ....
}


public class Login {

    private String password;
    private String userName;

    public Login(String info) {
        String[] values = info.split("&");
        for (String value : values) {
            String[] pair = value.split("=");
            if (pair.length == 2) {
                switch (pair[0]) {
                    case "password":
                        password = pair[1];
                        break;
                    case "userName":
                        userName = pair[1];
                        break;
                }
            }
        }
    }
}

But that method of transforming the Request Body into the object is really ugly and error prone and I would like to improve it.

Jandellis
  • 35
  • 2
  • 8

3 Answers3

3

First, convert the body to a map:

public Map<String, String> bodyToMap(String bodyStr) {
  Map<String, String> body = new HashMap<>();
  String[] values = bodyStr.split("&");
  for (String value : values) {
    String[] pair = value.split("=");
    if (pair.length == 2) {
      body.put(pair[0], pair[1]);
    }
  }
  return body;
}

Then use jackson's ObjectMapper to covert the map to your POJO

ObjectMapper mapper = new ObjectMapper();
Map<String, String> body = bodyToMap(info);
Login loginInfo = mapper.convertValue(body, Login.class);

You can check for more effective queryparam-style string to map converter here (You should, in case you have more legacy endpoints that have the same key like key1=value1_1&key1=value1_2&key2=value2_1) : Parse a URI String into Name-Value Collection

Hai Hoang
  • 1,207
  • 2
  • 18
  • 35
1

Spring boot will automagically map json to objects for you. Just delete the parens after your request body annotation, make your parameter type login and delete the constructor from login.

@ApiOperation(value = "To login", response = RequestStatus.class)
@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<ReturnValue> login(@RequestBody Login login) {
    ....
}

public class Login {

    private String password;
    private String userName;
    .... // getters
}
DCTID
  • 1,277
  • 9
  • 13
  • 1
    The problem is that he doesn't have JSON but a legacy string that's in the form of request query parameters. I don't remember Spring having a built-in parser to handle that format in a `RequestBody`, only as `RequestParam`s – rorschach Dec 11 '18 at 05:16
1

Add consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE to your method and also change you input parameter to a @RequestParam Map.

@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public @ResponseBody ResponseEntity<ReturnValue> login(@RequestParam Map<String, String> body) {
    String username = body.get("username");
    String password = body.get("password");

    //authenticate etc..

    return ResponseEntity.ok(returnValue);
}
Rentius2407
  • 1,108
  • 2
  • 11
  • 29
  • It is not quite what I am after, the request body has to change to `body=userName%3D1234%26password%3D1234` and the input Map becomes `"body" -> "userName=1234&password=1234"` – Jandellis Dec 11 '18 at 21:26
  • Hi Jandellis, no the input stays the same i.e. `username=theUsername&password=thePassword` no change needed. – Rentius2407 Dec 14 '18 at 08:54