Erstellen Sie eine REST-API mit Spring und Java Config

REST Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

Dieser Artikel zeigt, wie Sie REST im Frühjahr einrichten - die Controller- und HTTP-Antwortcodes, die Konfiguration des Payload-Marshalling und die Inhaltsverhandlung.

2. REST im Frühjahr verstehen

Das Spring-Framework unterstützt zwei Möglichkeiten zum Erstellen von RESTful-Services:

  • Verwenden von MVC mit ModelAndView
  • Verwenden von HTTP-Nachrichtenkonvertern

Der ModelAndView- Ansatz ist älter und viel besser dokumentiert, aber auch ausführlicher und konfigurationsintensiver. Es wird versucht, das REST-Paradigma in das alte Modell zu integrieren, was nicht ohne Probleme ist. Das Spring-Team hat dies verstanden und ab Spring 3.0 erstklassigen REST-Support bereitgestellt.

Der neue Ansatz, der auf HttpMessageConverter und Anmerkungen basiert , ist viel einfacher und einfacher zu implementieren. Die Konfiguration ist minimal und bietet sinnvolle Standardeinstellungen für das, was Sie von einem RESTful-Service erwarten würden.

3. Die Java-Konfiguration

@Configuration @EnableWebMvc public class WebConfig{ // }

Die neue Annotation @EnableWebMvc führt einige nützliche Dinge aus - insbesondere im Fall von REST erkennt sie die Existenz von Jackson und JAXB 2 im Klassenpfad und erstellt und registriert automatisch Standard-JSON- und XML-Konverter. Die Funktionalität der Annotation entspricht der XML-Version:

Dies ist eine Abkürzung, und obwohl sie in vielen Situationen nützlich sein kann, ist sie nicht perfekt. Wenn eine komplexere Konfiguration erforderlich ist, entfernen Sie die Anmerkung und erweitern Sie WebMvcConfigurationSupport direkt.

3.1. Spring Boot verwenden

Wenn wir die Annotation @SpringBootApplication verwenden und sich die spring-webmvc- Bibliothek im Klassenpfad befindet, wird die Annotation @EnableWebMvc automatisch mit einer automatischen Standardkonfiguration hinzugefügt.

Wir können dieser Konfiguration weiterhin MVC-Funktionalität hinzufügen, indem wir die WebMvcConfigurer- Schnittstelle in einer mit @Configuration annotierten Klasse implementieren . Wir können auch eine WebMvcRegistrationsAdapter- Instanz verwenden, um unsere eigenen RequestMappingHandlerMapping- , RequestMappingHandlerAdapter- oder ExceptionHandlerExceptionResolver- Implementierungen bereitzustellen .

Wenn Sie die MVC-Funktionen von Spring Boot verwerfen und eine benutzerdefinierte Konfiguration deklarieren möchten, können Sie dies mithilfe der Annotation @EnableWebMvc tun .

4. Testen des Federkontexts

Ab Spring 3.1 erhalten wir erstklassige Testunterstützung für @ Configuration- Klassen:

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

Wir geben die Java-Konfigurationsklassen mit der Annotation @ContextConfiguration an . Der neue AnnotationConfigContextLoader lädt die Bean-Definitionen aus den @ Configuration- Klassen.

Beachten Sie, dass die WebConfig- Konfigurationsklasse nicht im Test enthalten war, da sie in einem Servlet-Kontext ausgeführt werden muss, der nicht bereitgestellt wird.

4.1. Spring Boot verwenden

Spring Boot bietet verschiedene Anmerkungen, um den Spring ApplicationContext für unsere Tests intuitiver einzurichten .

Wir können nur einen bestimmten Teil der Anwendungskonfiguration laden oder den gesamten Kontextstartprozess simulieren.

Beispielsweise können wir die Annotation @SpringBootTest verwenden, wenn der gesamte Kontext erstellt werden soll, ohne den Server zu starten.

Nachdem dies geschehen ist , können wir @AutoConfigureMockMvc hinzufügen , um eine MockMvc- Instanz zu injizieren und HTTP-Anforderungen zu senden :

@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(...); } }

Um zu vermeiden, dass der gesamte Kontext erstellt wird und nur unsere MVC-Controller getestet werden, können wir @WebMvcTest verwenden:

@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(...); } }

Detaillierte Informationen zu diesem Thema finden Sie in unserem Artikel 'Testing in Spring Boot'.

5. Der Controller

Der @ RestController ist das zentrale Artefakt in der gesamten Webschicht der RESTful-API. Für den Zweck dieses Beitrags modelliert der Controller eine einfache REST-Ressource - 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); } }

Möglicherweise haben Sie bemerkt, dass ich ein einfaches RestPreconditions- Dienstprogramm im Guava-Stil verwende :

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

Die Controller-Implementierung ist nicht öffentlich - dies liegt daran, dass dies nicht erforderlich ist.

Normalerweise ist der Controller der letzte in der Abhängigkeitskette. Es empfängt HTTP-Anforderungen vom Spring Front Controller (dem DispatcherServlet ) und delegiert sie einfach an eine Serviceschicht weiter. Wenn es keinen Anwendungsfall gibt, in dem der Controller über eine direkte Referenz injiziert oder manipuliert werden muss, möchte ich ihn lieber nicht als öffentlich deklarieren.

Die Anforderungszuordnungen sind unkompliziert. Wie bei jedem Controller bestimmen der tatsächliche Wert des Mappings sowie die HTTP-Methode die Zielmethode für die Anforderung. @ RequestBody bindet die Parameter der Methode an den Hauptteil der HTTP-Anforderung, während @ResponseBody dasselbe für den Antwort- und Rückgabetyp tut.

Der @ RestController ist eine Abkürzung, um sowohl die @ ResponseBody- als auch die @ Controller- Annotationen in unsere Klasse aufzunehmen .

Sie stellen außerdem sicher, dass die Ressource mit dem richtigen HTTP-Konverter gemarshallt und nicht gemarshallt wird. Die Inhaltsverhandlung findet statt, um auszuwählen, welcher der aktiven Konverter verwendet wird, hauptsächlich basierend auf dem Accept- Header, obwohl auch andere HTTP-Header verwendet werden können, um die Darstellung zu bestimmen.

6. Zuordnen der HTTP-Antwortcodes

Die Statuscodes der HTTP-Antwort sind einer der wichtigsten Teile des REST-Dienstes, und das Thema kann schnell sehr kompliziert werden. Diese richtig zu machen kann das sein, was den Service macht oder bricht.

6.1. Nicht zugeordnete Anfragen

Wenn Spring MVC eine Anforderung ohne Zuordnung empfängt, betrachtet es die Anforderung als nicht zulässig und gibt eine 405-METHODE, die NICHT ERLAUBT ist, an den Client zurück.

Es wird auch empfohlen, den HTTP-Header " Zulassen" einzuschließen, wenn Sie einen 405 an den Client zurückgeben, um anzugeben, welche Vorgänge zulässig sind. Dies ist das Standardverhalten von Spring MVC und erfordert keine zusätzliche Konfiguration.

6.2. Gültige zugeordnete Anforderungen

Für jede Anfrage, die eine Zuordnung hat, betrachtet Spring MVC die Anfrage als gültig und antwortet mit 200 OK, wenn kein anderer Statuscode anders angegeben ist.

Aus diesem Grund deklariert der Controller unterschiedliche @ResponseStatus für die Aktionen zum Erstellen , Aktualisieren und Löschen , jedoch nicht für get , was in der Tat die Standardeinstellung 200 OK zurückgeben sollte.

6.3. Client-Fehler

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

In diesem Lernprogramm wurde veranschaulicht, wie ein REST-Service mithilfe der Spring- und Java-basierten Konfiguration implementiert und konfiguriert wird.

In den nächsten Artikeln der Reihe werde ich mich auf die Erkennbarkeit der API, die erweiterte Aushandlung von Inhalten und die Arbeit mit zusätzlichen Darstellungen einer Ressource konzentrieren.

Der gesamte Code dieses Artikels ist auf Github verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.

REST unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs