SpringとJava Configを使ってREST APIを作成する

SpringとJava Configを使用してREST APIを構築する

1. 概要

この記事では、set up REST in Spring –コントローラーとHTTP応答コード、ペイロードマーシャリングの構成、およびコンテンツネゴシエーションの方法を示します。

参考文献:

Spring @ResponseStatusを使用してHTTPステータスコードを設定する

@ResponseStatusアノテーションと、それを使用して応答ステータスコードを設定する方法をご覧ください。

Spring @Controllerおよび@RestControllerアノテーション

Spring MVCの@Controllerアノテーションと@RestControllerアノテーションの違いについて学びます。

2. SpringのRESTを理解する

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

  • ModelAndViewでMVCを使用する

  • HTTPメッセージコンバーターの使用

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

The new approach, based on HttpMessageConverter and annotations, is much more lightweight and easy to implement.構成は最小限であり、RESTfulサービスに期待するものに対して適切なデフォルトを提供します。

3. Java構成

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

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

これは近道であり、多くの状況で役立つかもしれませんが、完璧ではありません。 より複雑な構成が必要な場合は、アノテーションを削除し、WebMvcConfigurationSupportを直接拡張します。

3.1. Spring Bootを使用する

@SpringBootApplicationアノテーションを使用していて、spring-webmvc libraryがクラスパス上にある場合、@EnableWebMvcアノテーションはa default autoconfigurationとともに自動的に追加されます。

@Configuration annotatedクラスにWebMvcConfigurerインターフェースを実装することで、この構成にMVC機能を追加できます。 WebMvcRegistrationsAdapterインスタンスを使用して、独自のRequestMappingHandlerMappingRequestMappingHandlerAdapter、または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 Bootは、より直感的な方法でテスト用のSpringApplicationContextを設定するためのいくつかのアノテーションを提供します。

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

たとえば、サーバーを起動せずにコンテキスト全体を作成する場合は、@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(...);
    }
}

この主題に関する詳細情報はour ‘Testing in Spring Boot' articleにあります。

5. コントローラー

The @RestController is the central artifact in the entire Web Tier of the RESTful API.この投稿の目的のために、コントローラーは単純なRESTリソースをモデル化しています–Foo

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

    @Autowired
    private IFooService service;

    @GetMapping
    public List 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 checkFound(T resource) {
        if (resource == null) {
            throw new MyResourceNotFoundException();
        }
        return resource;
    }
}

コントローラの実装は非公開です。これは、公開する必要がないためです。

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

要求のマッピングは簡単です。 As with any controller, the actual value of the mapping, as well as the HTTP method, determine the target method for the request. @RequestBodyは、メソッドのパラメーターをHTTP要求の本体にバインドしますが、@ResponseBodyは、応答と戻り値の型に対して同じことを行います。

@RestController shorthandであり、クラス.@ResponseBody @Controllerの両方のアノテーションが含まれます。

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

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

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

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

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

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

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

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

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

6.3. クライアントエラー

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

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

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

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

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

6.4. @ExceptionHandlerの使用

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

もちろん、SpringとSpring Bootの両方に、より柔軟性のあるways to handle errorsがあります。

7. Additional Maven Dependencies **

spring-webmvc依存関係required for the standard web applicationに加えて、RESTAPIのコンテンツマーシャリングとアンマーシャリングを設定する必要があります。


   
      com.fasterxml.jackson.core
      jackson-databind
      2.9.8
   
   
      javax.xml.bind
      jaxb-api
      2.3.1
      runtime
   

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

7.1. Spring Bootを使用する

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

自動設定は、クラスパスにマッピングライブラリを含めるだけで実行されます。

通常、Webアプリケーションを開発している場合、we’ll just add the spring-boot-starter-web dependency and rely on it to include all the necessary artifacts to our project


    org.springframework.boot
    spring-boot-starter-web
    2.1.2.RELEASE

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

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

8. 結論

このチュートリアルでは、SpringおよびJavaベースの構成を使用してRESTサービスを実装および構成する方法を示しました。

シリーズの次の記事では、Discoverability of the API、高度なコンテンツネゴシエーション、およびResource.の追加表現の操作に焦点を当てます。

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