0

I'm developing a REST web application using Betfair's API, so I need to serialize/deserialize JSON in order to send HttpRequest and read HttpResponse.

I'm using Spring MVC so I want to use Jackson with @RestController, @RequestBody and @ResponseBody but I'm struggling to understand how they work.

In the following example I'm sending a POST request to Betfair to obtain user's firstname and lastname. I think that the HTTP Response should be deserialized into my model object AccountDetailsResponse, because I'm using @RequestBody annotation, but no value is shown in the home web page (only "Welcome, "). No exception is thrown.

Controller

@EnableWebMvc
@RestController
public class LoginController {
    private final String APP_KEY = "myAppKey";
    private final String LOGIN_END_POINT = "https://identitysso.betfair.it/api/login";
    private final String LOGOUT_END_POINT = "https://identitysso.betfair.it/api/logout";
    private final String ACCOUNT_END_POINT = "https://api.betfair.com/exchange/account/json-rpc/v1";
    private String token;

    @RequestMapping(value = "/index", method = RequestMethod.POST, params = { "firstName", "lastName" })
    public ModelAndView getAccountDetails(User user, @RequestBody AccountDetailsResponse accountDetails) {
        String response = null;

        JsonRequest jsonRequest = new JsonRequest();
        jsonRequest.setJsonrpc("2.0");
        jsonRequest.setMethod("AccountAPING/v1.0/getAccountDetails");
        jsonRequest.setId("1");

        response = HttpRequestHandler.sendRequest("POST", ACCOUNT_END_POINT, APP_KEY, jsonRequest);

        if(response.equals("ERROR")) {
            String message = "Error";
            return new ModelAndView("home", "message", message);
        }

        else {
            user.setFirstName(accountDetails.getResult().getFirstName());
            user.setLastName(accountDetails.getResult().getLastName());

            return new ModelAndView("home", "user", user);
        }
    }
}

N.B. I've omitted the showLoginForm (value = "/index", method = RequestMethod.GET) and doLogin (value = "/index", method = RequestMethod.POST, params = { "email", "password" })

HttpRequestHandler

public static String sendPostRequest(String url, String APP_KEY, JsonRequest jsonRequest) {
    try {
        URL urlRequest = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) urlRequest.openConnection();

        connection.setRequestMethod("POST");
        connection.addRequestProperty("Accept", "application/json");
        connection.addRequestProperty("X-Application", APP_KEY);

        int responseCode = connection.getResponseCode();

        if(responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }

            in.close();

            if(response.toString().contains("error")) {
                return "ERROR";
            }
            else
                return response.toString();
        }
        else
            return "ERROR";

    } 
    catch (MalformedURLException e) {
        return "ERROR";
    } 
    catch (IOException e) {
        return "ERROR";
    }
}

home.html

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>RestController test</title>
</head>
<body>

<span th:if = "${user.logged}">
Welcome, <span th:text = "${user.firstName}"></span> <span th:text = "${user.lastName}"></span>
</span>

<p><span th:text = "${message}"></span></p>

</body>
</html>

User

public class User {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    private Double toBetBalance;
    private boolean logged;

    public User() { }

    //getters and setters
}

AccountDetailsResponse

public class AccountDetailsResponse {
    private String jsonrpc;
    private Result result;
    private String id;

    public AccountDetailsResponse() { }

    //getters and setters
}

Result

public class Result {
    private String currencyCode;
    private String firstName;
    private String lastName;
    private String localeCode;
    private String region;
    private String timezone;
    private Float discountRate;
    private Integer pointsBalance;
    private String countryCode;

    public Result() { }

    //getters and setters
}

JSON response from Betfair

[  
   {  
      "jsonrpc":"2.0",
      "result":{  
         "currencyCode":"EUR",
         "firstName":"My name",
         "lastName":"My lastname",
         "localeCode":"it_IT",
         "region":"GBR",
         "timezone":"CET",
         "discountRate":0.0,
         "pointsBalance":0,
         "countryCode":"IT"
      },
      "id":1
   }
]

POM dependencies

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>4.2.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf</artifactId>
        <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>2.1.4.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.8.2</version>
    </dependency>

</dependencies>

Where am I wrong?

andy
  • 269
  • 1
  • 12
  • 22

2 Answers2

0

Shouldn't you be doing

response.getResult().getFirstName(); 
response.getResult().getLastName();

in the LoginController 's else block?

user3138997
  • 230
  • 1
  • 10
  • response is a String, not an AccountDetailsResponse object. I could use Jackson's ObjectMapper to bind response to a JsonNode, but that's not my aim, because in Spring MVC serialization and deserialization should work automatically using @RestController – andy Sep 12 '16 at 18:48
  • From Spring docs - "The @RequestBody method parameter annotation indicates that a method parameter should be bound to the value of the HTTP request body." Its purpose is basically to collect the request params in to your domain class. In your case it helps in mapping email and password in to User object. – user3138997 Sep 12 '16 at 18:58
  • email and password are just mapped in to User object thanks to doLogin method. Here my RequestBody is AccountDetailsResponse object and the Betfair response is the one I posted above. From that JSON i want to take firstName and lastName fields. Since the JSON corresponds with the AccountDetailsResponse object, that should be mapped in to the latter object, no? – andy Sep 12 '16 at 19:11
  • sorry, now I understand. it could be down to the header set in the http. It should be set with Content-type = application/json.. Spring needs this to choose one of its default message converters while mapping the json in the req body on to the @RequestBody AccountDetailsResponse.. good luck – user3138997 Sep 12 '16 at 20:30
  • I already used the Accept: application/json header, but also changing or adding Content-Type doesn't work, unfortunately – andy Sep 13 '16 at 17:06
0

What OP needs is a Spring resource called RestTemplate. That's used to make a request to a URL, which response can be bound to a custom class, with Jackson taking care of the deserialization.

Take a look at the exchange method.

StatelessDev
  • 294
  • 4
  • 15