Bangun REST API dengan Spring dan Java Config

REST Top

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Ikhtisar

Artikel ini menunjukkan cara menyiapkan REST di Spring - kode Pengontrol dan respons HTTP, konfigurasi marshalling muatan, dan negosiasi konten.

2. Memahami REST di Spring

Framework Spring mendukung dua cara untuk membuat layanan RESTful:

  • menggunakan MVC dengan ModelAndView
  • menggunakan pengonversi pesan HTTP

The ModelAndView pendekatan lebih tua dan jauh lebih baik didokumentasikan, tetapi juga lebih verbose dan konfigurasi berat. Ia mencoba untuk memasukkan paradigma REST ke dalam model lama, yang bukannya tanpa masalah. Tim Spring memahami hal ini dan memberikan dukungan REST kelas satu yang dimulai dengan Spring 3.0.

Pendekatan baru, berdasarkan HttpMessageConverter dan anotasi, jauh lebih ringan dan mudah diterapkan. Konfigurasi minimal, dan ini memberikan default yang masuk akal untuk apa yang Anda harapkan dari layanan RESTful.

3. Konfigurasi Java

@Configuration @EnableWebMvc public class WebConfig{ // }

Anotasi @EnableWebMvc yang baru melakukan beberapa hal berguna - khususnya, dalam kasus REST, anotasi ini mendeteksi keberadaan Jackson dan JAXB 2 di classpath dan secara otomatis membuat serta mendaftarkan konverter JSON dan XML default. Fungsionalitas anotasi ini setara dengan versi XML:

Ini adalah jalan pintas, dan meskipun mungkin berguna dalam banyak situasi, ini tidak sempurna. Jika konfigurasi yang lebih kompleks diperlukan, hapus anotasi dan perpanjang WebMvcConfigurationSupport secara langsung.

3.1. Menggunakan Spring Boot

Jika kita menggunakan anotasi @SpringBootApplication dan pustaka spring-webmvc ada di jalur kelas, anotasi @EnableWebMvc ditambahkan secara otomatis dengan konfigurasi otomatis default.

Kami masih dapat menambahkan fungsionalitas MVC ke konfigurasi ini dengan mengimplementasikan antarmuka WebMvcConfigurer pada kelas beranotasi @Configuration . Kita juga bisa menggunakan instance WebMvcRegistrationsAdapter untuk menyediakan implementasi RequestMappingHandlerMapping , RequestMappingHandlerAdapter , atau ExceptionHandlerExceptionResolver kita sendiri .

Terakhir, jika kita ingin membuang fitur MVC Spring Boot dan mendeklarasikan konfigurasi kustom, kita dapat melakukannya dengan menggunakan anotasi @EnableWebMvc .

4. Menguji Konteks Pegas

Dimulai dengan Spring 3.1, kami mendapatkan dukungan pengujian kelas satu untuk kelas @Configuration :

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = {WebConfig.class, PersistenceConfig.class}, loader = AnnotationConfigContextLoader.class) public class SpringContextIntegrationTest { @Test public void contextLoads(){ // When } }

Kami menentukan kelas konfigurasi Java dengan anotasi @ContextConfiguration . AnnotationConfigContextLoader baru memuat definisi kacang dari kelas @Configuration .

Perhatikan bahwa kelas konfigurasi WebConfig tidak disertakan dalam pengujian karena perlu dijalankan dalam konteks Servlet, yang tidak tersedia.

4.1. Menggunakan Spring Boot

Spring Boot menyediakan beberapa anotasi untuk menyiapkan Spring ApplicationContext untuk pengujian kami dengan cara yang lebih intuitif.

Kita hanya dapat memuat bagian tertentu dari konfigurasi aplikasi, atau kita dapat mensimulasikan seluruh proses permulaan konteks.

Misalnya, kita dapat menggunakan anotasi @SpringBootTest jika kita ingin seluruh konteks dibuat tanpa memulai server.

Dengan itu, kita kemudian dapat menambahkan @AutoConfigureMockMvc untuk memasukkan instance MockMvc dan mengirim permintaan 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(...); } }

Untuk menghindari pembuatan seluruh konteks dan hanya menguji Pengontrol MVC, kita dapat menggunakan @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(...); } }

Kami dapat menemukan informasi rinci tentang subjek ini di artikel 'Pengujian di Spring Boot' kami.

5. Pengendali

The @RestController adalah artefak sentral dalam seluruh Tier Web API tenang. Untuk tujuan posting ini, pengontrol memodelkan sumber daya REST sederhana - 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); } }

Anda mungkin telah memperhatikan bahwa saya menggunakan utilitas RestPreconditions gaya Guava yang sederhana :

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

Implementasi Kontroler bersifat non-publik - ini karena tidak perlu.

Biasanya, pengontrol adalah yang terakhir dalam rantai ketergantungan. Ini menerima permintaan HTTP dari pengontrol depan Spring ( DispatcherServlet ) dan hanya mendelegasikannya ke lapisan layanan. Jika tidak ada kasus penggunaan di mana pengontrol harus disuntikkan atau dimanipulasi melalui referensi langsung, maka saya memilih untuk tidak mendeklarasikannya sebagai publik.

Pemetaan permintaan sangat mudah. Seperti halnya pengontrol apa pun, nilai pemetaan yang sebenarnya, serta metode HTTP, menentukan metode target untuk permintaan tersebut. @ RequestBody akan mengikat parameter metode ke badan permintaan HTTP, sedangkan @ResponseBody melakukan hal yang sama untuk jenis respons dan kembalian.

The @RestController adalah singkatan untuk menyertakan kedua @ResponseBody dan @Controller penjelasan di kelas kami .

They also ensure that the resource will be marshalled and unmarshalled using the correct HTTP converter. Content negotiation will take place to choose which one of the active converters will be used, based mostly on the Accept header, although other HTTP headers may be used to determine the representation as well.

6. Mapping the HTTP Response Codes

The status codes of the HTTP response are one of the most important parts of the REST service, and the subject can quickly become very complicated. Getting these right can be what makes or breaks the service.

6.1. Unmapped Requests

If Spring MVC receives a request which doesn't have a mapping, it considers the request not to be allowed and returns a 405 METHOD NOT ALLOWED back to the client.

It's also a good practice to include the Allow HTTP header when returning a 405 to the client, to specify which operations are allowed. This is the standard behavior of Spring MVC and doesn't require any additional configuration.

6.2. Valid Mapped Requests

For any request that does have a mapping, Spring MVC considers the request valid and responds with 200 OK if no other status code is specified otherwise.

It's because of this that the controller declares different @ResponseStatus for the create, update and delete actions but not for get, which should indeed return the default 200 OK.

6.3. Client Error

In the case of a client error, custom exceptions are defined and mapped to the appropriate error codes.

Simply throwing these exceptions from any of the layers of the web tier will ensure Spring maps the corresponding status code on the HTTP response:

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

These exceptions are part of the REST API and, as such, should only be used in the appropriate layers corresponding to REST; if for instance, a DAO/DAL layer exists, it should not use the exceptions directly.

Note also that these are not checked exceptions but runtime exceptions – in line with Spring practices and idioms.

6.4. Using @ExceptionHandler

Another option to map custom exceptions on specific status codes is to use the @ExceptionHandler annotation in the controller. The problem with that approach is that the annotation only applies to the controller in which it's defined. This means that we need to declares in each controller individually.

Of course, there are more ways to handle errors in both Spring and Spring Boot that offer more flexibility.

7. Additional Maven Dependencies

In addition to the spring-webmvc dependency required for the standard web application, we'll need to set up content marshalling and unmarshalling for the REST API:

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

These are the libraries used to convert the representation of the REST resource to either JSON or XML.

7.1. Using Spring Boot

If we want to retrieve JSON-formatted resources, Spring Boot provides support for different libraries, namely Jackson, Gson and JSON-B.

Auto-configuration is carried out by just including any of the mapping libraries in the classpath.

Usually, if we're developing a web application, 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 uses Jackson by default.

If we want to serialize our resources in an XML format, we'll have to add the Jackson XML extension (jackson-dataformat-xml) to our dependencies, or fallback to the JAXB implementation (provided by default in the JDK) by using the @XmlRootElement annotation on our resource.

8. Conclusion

Tutorial ini mengilustrasikan cara mengimplementasikan dan mengonfigurasi Layanan REST menggunakan konfigurasi berbasis Spring dan Java.

Di artikel seri berikutnya, saya akan fokus pada API untuk dapat ditemukan, negosiasi konten lanjutan, dan bekerja dengan representasi tambahan dari Sumber Daya.

Semua kode artikel ini tersedia di Github. Ini adalah proyek berbasis Maven, jadi semestinya mudah untuk mengimpor dan menjalankan apa adanya.

REST bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya