1. 序章

このクイックチュートリアルでは、Springの RestTemplate を使用して、JSONコンテンツを送信するPOSTリクエストを作成する方法を説明します。

2. 例の設定

投稿するデータを表す単純なPersonモデルクラスを追加することから始めましょう。

public class Person {
    private Integer id;
    private String name;

    // standard constructor, getters, setters
}

Person オブジェクトを操作するために、PersonServiceインターフェイスと2つのメソッドを使用した実装を追加します。

public interface PersonService {

    public Person saveUpdatePerson(Person person);
    public Person findPersonById(Integer id);
}

これらのメソッドの実装は、単にオブジェクトを返します。 ここでは、このレイヤーのダミー実装を使用しているため、Webレイヤーに焦点を当てることができます。

3. RESTAPIセットアップ

Personクラスの単純なRESTAPIを定義しましょう。

@PostMapping(
  value = "/createPerson", consumes = "application/json", produces = "application/json")
public Person createPerson(@RequestBody Person person) {
    return personService.saveUpdatePerson(person);
}

@PostMapping(
  value = "/updatePerson", consumes = "application/json", produces = "application/json")
public Person updatePerson(@RequestBody Person person, HttpServletResponse response) {
    response.setHeader("Location", ServletUriComponentsBuilder.fromCurrentContextPath()
      .path("/findPerson/" + person.getId()).toUriString());
    
    return personService.saveUpdatePerson(person);
}

データをJSON形式で投稿したいことを忘れないでください。 そのために、 @PostMappingアノテーションにconsumes属性を追加し、両方のメソッドの値を「application/json」にしました。

同様に、produces属性を「application/ json」に設定して、JSON形式の応答本文が必要であることをSpringに通知します。

両方のメソッドについて、personパラメーターに@RequestBodyアノテーションを付けました。 これにより、personオブジェクトがHTTPリクエストの本体にバインドされることがSpringに通知されます。

最後に、どちらのメソッドも、応答本文にバインドされるPersonオブジェクトを返します。 APIクラスに@RestControllerアノテーションを付けて、すべてのAPIメソッドに非表示の@ResponseBodyアノテーションを付けることに注意してください。

4. RestTemplateを使用する

これで、 Person RESTAPIをテストするためのいくつかの単体テストを作成できます。 ここでは、 RestTemplateによって提供されるPOSTメソッド(postForObject、postForEntity、およびpostForLocation)を使用して、PersonAPIにPOSTリクエストを送信しようとします。

単体テストの実装を開始する前に、すべての単体テストメソッドで使用するオブジェクトを初期化するためのセットアップメソッドを定義しましょう。

@BeforeClass
public static void runBeforeAllTestMethods() {
    createPersonUrl = "http://localhost:8082/spring-rest/createPerson";
    updatePersonUrl = "http://localhost:8082/spring-rest/updatePerson";

    restTemplate = new RestTemplate();
    headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    personJsonObject = new JSONObject();
    personJsonObject.put("id", 1);
    personJsonObject.put("name", "John");
}

このセットアップメソッドに加えて、単体テストでJSON文字列をJSONNodeオブジェクトに変換するために次のマッパーを参照することに注意してください。

private final ObjectMapper objectMapper = new ObjectMapper();

前述のように、 データをJSON形式で投稿したいと思います。 これを実現するために、APPLICATION_JSONメディアタイプを使用してContent-Typeヘッダーをリクエストに追加します。

SpringのHttpHeadersクラスは、ヘッダーにアクセスするためのさまざまなメソッドを提供します。 ここでは、 setContentType メソッドを呼び出して、Content-Typeヘッダーをapplication/jsonに設定します。 headersオブジェクトをリクエストに添付します。

4.1. postForObjectを使用したJSONの投稿

RestTemplatepostForObjectメソッドは、指定されたURIテンプレートにオブジェクトを投稿することにより、新しいリソースを作成します。 responseTypeパラメーターで指定されたタイプに自動的に変換された結果を返します。

Person APIにPOSTリクエストを送信して、新しい Person オブジェクトを作成し、この新しく作成されたオブジェクトを応答で返すとします。

まず、personJsonObjectと、 コンテンツタイプこれにより、 postForObject JSONリクエスト本文を送信するメソッド:

@Test
public void givenDataIsJson_whenDataIsPostedByPostForObject_thenResponseBodyIsNotNull()
  throws IOException {
    HttpEntity<String> request = 
      new HttpEntity<String>(personJsonObject.toString(), headers);
    
    String personResultAsJsonStr = 
      restTemplate.postForObject(createPersonUrl, request, String.class);
    JsonNode root = objectMapper.readTree(personResultAsJsonStr);
    
    assertNotNull(personResultAsJsonStr);
    assertNotNull(root);
    assertNotNull(root.path("name").asText());
}

postForObject()メソッドは、応答本文をStringタイプとして返します。

responseType パラメーターを設定することにより、応答をPersonオブジェクトとして返すこともできます。

Person person = restTemplate.postForObject(createPersonUrl, request, Person.class);

assertNotNull(person);
assertNotNull(person.getName());

実際、 createPersonUrl URIと一致するリクエストハンドラーメソッドは、JSON形式でレスポンスの本文を生成します。

ただし、これは私たちの制限ではありません— postForObject は、応答本文を要求されたJavaタイプに自動的に変換できます(例: String Person responseTypeパラメーターで指定されます。

4.2. postForEntityを使用したJSONの投稿

postForObject()と比較すると、 postForEntity()は応答をResponseEntityオブジェクトとして返します。それ以外は、両方のメソッドが同じジョブを実行します。

Person APIにPOSTリクエストを送信して、新しい Person オブジェクトを作成し、応答をResponseEntityとして返すとします。

postForEntity メソッドを使用して、これを実装できます。

@Test
public void givenDataIsJson_whenDataIsPostedByPostForEntity_thenResponseBodyIsNotNull()
  throws IOException {
    HttpEntity<String> request = 
      new HttpEntity<String>(personJsonObject.toString(), headers);
    
    ResponseEntity<String> responseEntityStr = restTemplate.
      postForEntity(createPersonUrl, request, String.class);
    JsonNode root = objectMapper.readTree(responseEntityStr.getBody());
 
    assertNotNull(responseEntityStr.getBody());
    assertNotNull(root.path("name").asText());
}

postForObject と同様に、postForEntityにはresponseTypeパラメーターがあり、応答本文を要求されたJavaタイプに変換します。

ここでは、応答本文を次のように返すことができました。 ResponseEntity

応答を次のように返すこともできます ResponseEntity を設定してオブジェクト responseType パラメータから Person.class

ResponseEntity<Person> responseEntityPerson = restTemplate.
  postForEntity(createPersonUrl, request, Person.class);
 
assertNotNull(responseEntityPerson.getBody());
assertNotNull(responseEntityPerson.getBody().getName());

4.3. postForLocationを使用したJSONの投稿

postForObjectおよびpostForEntityメソッドと同様に、 postForLocation も、指定されたオブジェクトを指定されたURIに投稿することによって新しいリソースを作成します。 唯一の違いは、Locationヘッダーの値を返すことです。

上記のupdatePersonRESTAPIメソッドで応答のLocationヘッダーを設定する方法をすでに見てきたことを思い出してください。

response.setHeader("Location", ServletUriComponentsBuilder.fromCurrentContextPath()
  .path("/findPerson/" + person.getId()).toUriString());

ここで、投稿したpersonオブジェクトを更新した後、応答のLocationヘッダーを返したいと想像してみましょう。

これは、postForLocationメソッドを使用して実装できます。

@Test
public void givenDataIsJson_whenDataIsPostedByPostForLocation_thenResponseBodyIsTheLocationHeader() 
  throws JsonProcessingException {
    HttpEntity<String> request = new HttpEntity<String>(personJsonObject.toString(), headers);
    URI locationHeader = restTemplate.postForLocation(updatePersonUrl, request);
    
    assertNotNull(locationHeader);
}

5. 結論

この記事では、RestTemplateを使用してJSONでPOSTリクエストを行う方法について説明しました。

いつものように、すべての例とコードスニペットはGitHubにあります。