1. 概要

このチュートリアルでは、 SpringでRESTをセットアップする方法、コントローラーとHTTP応答コード、ペイロードマーシャリングの構成、コンテンツネゴシエーションなどを学習します。

2. SpringのRESTを理解する

Springフレームワークは、RESTfulサービスを作成する2つの方法をサポートしています。

  • ModelAndViewでMVCを使用する
  • HTTPメッセージコンバーターの使用

ModelAndView アプローチは古く、文書化されていますが、より冗長で構成が重いです。 それは、RESTパラダイムを古いモデルに押し込もうとしますが、問題がないわけではありません。 Springチームはこれを理解し、Spring3.0以降でファーストクラスのRESTサポートを提供しました。

HttpMessageConverterとアノテーションに基づく新しいアプローチは、はるかに軽量で実装が簡単です。構成は最小限であり、RESTfulサービスに期待されるものに適切なデフォルトを提供します。

3. Java構成

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

新しい@EnableWebMvcアノテーションはいくつかの便利なことを行います。 具体的には、RESTの場合、クラスパスにJacksonとJAXB 2が存在することを検出し、デフォルトのJSONおよびXMLコンバーターを自動的に作成して登録します。 注釈の機能は、XMLバージョンと同等です。

これはショートカットであり、多くの状況で役立つ可能性がありますが、完全ではありません。 より複雑な構成が必要な場合は、アノテーションを削除してWebMvcConfigurationSupportを直接拡張できます。

3.1. スプリングブートの使用

@SpringBootApplication アノテーションを使用していて、 spring-webmvc ライブラリがクラスパス上にある場合、@EnableWebMvcアノテーションはデフォルトの自動構成

@Configuration注釈付きクラスにWebMvcConfigurerインターフェイスを実装することで、この構成にMVC機能を追加できます。 WebMvcRegistrationsAdapter インスタンスを使用して、独自の RequestMappingHandlerMapping RequestMappingHandlerAdapter 、またはExceptionHandlerExceptionResolver実装を提供することもできます。

最後に、Spring BootのMVC機能を破棄してカスタム構成を宣言する場合は、@EnableWebMvcアノテーションを使用して宣言できます。

4. Springコンテキストのテスト

Spring 3.1以降、@Configurationクラスのファーストクラスのテストサポートが提供されます。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( 
  classes = {WebConfig.class, PersistenceConfig.class},
  loader = AnnotationConfigContextLoader.class)
public class SpringContextIntegrationTest {

   @Test
   public void contextLoads(){
      // When
   }
}

@ContextConfigurationアノテーションを使用してJava構成クラスを指定しています。 新しいAnnotationConfigContextLoaderは、@ConfigurationクラスからBean定義をロードします。

WebConfig 構成クラスは、提供されていないサーブレットコンテキストで実行する必要があるため、テストに含まれていないことに注意してください。

4.1. スプリングブートの使用

Spring Bootは、より直感的な方法でテスト用にSpring ApplicationContextを設定するためのいくつかのアノテーションを提供します。

アプリケーション構成の特定のスライスのみをロードすることも、コンテキスト起動プロセス全体をシミュレートすることもできます。

たとえば、サーバーを起動せずにコンテキスト全体を作成する場合は、@SpringBootTestアノテーションを使用できます。

これで、 @AutoConfigureMockMvc を追加して、 MockMvc インスタンスを挿入し、HTTPリクエストを送信できます

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class FooControllerAppIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenTestApp_thenEmptyResponse() throws Exception {
        this.mockMvc.perform(get("/foos")
            .andExpect(status().isOk())
            .andExpect(...);
    }

}

コンテキスト全体の作成を回避し、MVCコントローラーのみをテストするために、 @WebMvcTest:を使用できます。

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerWebLayerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private IFooService service;

    @Test()
    public void whenTestMvcController_thenRetrieveExpectedResult() throws Exception {
        // ...

        this.mockMvc.perform(get("/foos")
            .andExpect(...);
    }
}

このテーマの詳細については、の「Spring Bootでのテスト」の記事を参照してください。

5. コントローラー

@RestControllerは、RESTful APIのWeb層全体の中心的なアーティファクトです。この記事の目的のために、コントローラーは単純なRESTリソースFooをモデル化しています。

@RestController
@RequestMapping("/foos")
class FooController {

    @Autowired
    private IFooService service;

    @GetMapping
    public List<Foo> findAll() {
        return service.findAll();
    }

    @GetMapping(value = "/{id}")
    public Foo findById(@PathVariable("id") Long id) {
        return RestPreconditions.checkFound(service.findById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Long create(@RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        return service.create(resource);
    }

    @PutMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void update(@PathVariable( "id" ) Long id, @RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        RestPreconditions.checkNotNull(service.getById(resource.getId()));
        service.update(resource);
    }

    @DeleteMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void delete(@PathVariable("id") Long id) {
        service.deleteById(id);
    }

}

ご覧のとおり、単純なGuavaスタイルのRestPreconditionsユーティリティを使用しています。

public class RestPreconditions {
    public static <T> T checkFound(T resource) {
        if (resource == null) {
            throw new MyResourceNotFoundException();
        }
        return resource;
    }
}

Controllerの実装は、公開する必要がないため、公開されていません。

通常、コントローラーは依存関係のチェーンの最後です。 Springフロントコントローラー( DispatcherServlet )からHTTPリクエストを受信し、それらをサービスレイヤーに転送するだけです。 コントローラーを直接参照して注入または操作する必要があるユースケースがない場合は、コントローラーをパブリックとして宣言しないことをお勧めします。

リクエストのマッピングは簡単です。他のコントローラーと同様に、マッピングの実際の値とHTTPメソッドによって、リクエストのターゲットメソッドが決まります。 @ RequestBodyはメソッドのパラメータをHTTPリクエストの本文に追加しますが、@ResponseBodyはレスポンスとリターンのタイプに対して同じことを行います。

@RestController は、省略形であり、@ResponseBody@Controllerアノテーションの両方をクラスに含めます。[X154X ]

また、正しいHTTPコンバーターを使用して、リソースがマーシャリングおよびアンマーシャリングされることを保証します。 コンテントネゴシエーションは、主に Accept ヘッダーに基づいて、アクティブなコンバーターのどれを使用するかを選択するために行われますが、他のHTTPヘッダーを使用して表現を決定することもできます。

6. HTTP応答コードのマッピング

HTTP応答のステータスコードはRESTサービスの最も重要な部分の1つであり、サブジェクトはすぐに非常に複雑になる可能性があります。 これらを正しくすることは、サービスを成功または失敗させるものになる可能性があります。

6.1. マップされていないリクエスト

Spring MVCがマッピングのないリクエストを受信した場合、そのリクエストは許可されていないと見なし、405 METHODNOTALLOWEDをクライアントに返します。

405 をクライアントに返すときに、 Allow HTTPヘッダーを含めて、許可する操作を指定することもお勧めします。 これはSpringMVCの標準的な動作であり、追加の構成は必要ありません。

6.2. 有効なマップされたリクエスト

マッピングがあるリクエストの場合、Spring MVCはリクエストが有効であると見なし、他のステータスコードが指定されていない場合は、200OKで応答します。

このため、コントローラーは create update deleteアクションに対して異なる@ResponseStatusを宣言しますが、に対しては宣言しません。 get。これは実際にデフォルトの200OKを返すはずです。

6.3. クライアントエラー

クライアントエラーの場合、カスタム例外が定義され、適切なエラーコードにマップされます。

Web層のいずれかのレイヤーからこれらの例外をスローするだけで、Springは対応するステータスコードをHTTP応答にマップします。

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
   //
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
   //
}

これらの例外はRESTAPIの一部であるため、RESTに対応する適切なレイヤーでのみ使用する必要があります。 たとえば、DAO / DAL層が存在する場合、例外を直接使用するべきではありません。

これらはチェックされた例外ではなく、Springのプラクティスとイディオムに沿ったランタイム例外であることに注意してください。

6.4. @ExceptionHandlerを使用する

カスタム例外を特定のステータスコードにマップする別のオプションは、コントローラーで@ExceptionHandlerアノテーションを使用することです。 このアプローチの問題は、アノテーションが定義されているコントローラーにのみ適用されることです。 これは、各コントローラーで個別に宣言する必要があることを意味します。

もちろん、SpringとSpring Bootの両方で、エラーを処理する方法が増え、柔軟性が向上します。

7. 追加のMaven依存関係

標準のWebアプリケーションに必要なspring-webmvc依存関係に加えて、RESTAPIのコンテンツマーシャリングとアンマーシャリングを設定する必要があります。

<dependencies>
   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.8</version>
   </dependency>
   <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

これらは、RESTリソースの表現をJSONまたはXMLに変換するために使用するライブラリです。

7.1. スプリングブートの使用

JSON形式のリソースを取得する場合、Spring Bootは、Jackson、Gson、JSON-Bなどのさまざまなライブラリをサポートします。

クラスパスにマッピングライブラリを含めるだけで、自動構成を実行できます。

通常、Webアプリケーションを開発している場合は、 spring-boot-starter-web依存関係を追加し、それに依存して、プロジェクトに必要なすべてのアーティファクトを含めます

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.6.1</version>
</dependency>

SpringBootはデフォルトでJacksonを使用します。

リソースをXML形式でシリアル化する場合は、Jackson XML拡張機能( jackson-dataformat-xml )を依存関係に追加するか、JAXB実装(デフォルトでJDK)リソースの@XmlRootElementアノテーションを使用します。

8. 結論

この記事では、SpringおよびJavaベースの構成を使用してRESTサービスを実装および構成する方法について説明しました。

シリーズの次の記事では、 APIの発見可能性、高度なコンテンツネゴシエーション、およびリソースの追加表現の操作に焦点を当てます。

この記事のすべてのコードは、Githubから入手できます。 これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。