Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:
>> ПРЕГЛЕД НА КУРСА1. Общ преглед
Тази статия показва как да настроите REST през пролетта - кодовете за отговор на контролера и HTTP, конфигуриране на разпределението на полезния товар и договаряне на съдържание.
2. Разбиране на REST през пролетта
Spring рамката поддържа два начина за създаване на RESTful услуги:
- използване на MVC с ModelAndView
- с помощта на преобразуватели на HTTP съобщения
Подходът ModelAndView е по-стар и много по-добре документиран, но също така по-подробен и по-тежък в конфигурацията. Той се опитва да включи парадигмата REST в стария модел, който не е безпроблемен. Екипът на Spring разбра това и предостави първокласна поддръжка на REST, започвайки с Spring 3.0.
Новият подход, базиран на HttpMessageConverter и анотации, е много по-лек и лесен за изпълнение. Конфигурацията е минимална и осигурява разумни настройки по подразбиране за това, което бихте очаквали от RESTful услуга.
3. Конфигурацията на Java
@Configuration @EnableWebMvc public class WebConfig{ // }
Новата анотация @EnableWebMvc прави някои полезни неща - по-специално, в случая с REST, тя открива съществуването на Jackson и JAXB 2 в пътя на класа и автоматично създава и регистрира JSON и XML конвертори по подразбиране. Функционалността на анотацията е еквивалентна на версията XML:
Това е пряк път и въпреки че може да бъде полезен в много ситуации, не е перфектен. Когато е необходима по-сложна конфигурация, премахнете анотацията и разширете WebMvcConfigurationSupport директно.
3.1. Използване на Spring Boot
Ако използвате @SpringBootApplication анотация и пролетно-webmvc библиотеката е на CLASSPATH, тогава @EnableWebMvc анотацията се добавя автоматично с автоматичната конфигурация по подразбиране.
Все още можем да добавим MVC функционалност за тази конфигурация, като реализират WebMvcConfigurer интерфейса на @Configuration анотиран клас. Също така можем да използваме екземпляр WebMvcRegistrationsAdapter, за да предоставим собствени реализации на RequestMappingHandlerMapping , RequestMappingHandlerAdapter или ExceptionHandlerExceptionResolver .
И накрая, ако искаме да отхвърлим MVC характеристиките на Spring Boot и да декларираме персонализирана конфигурация, можем да го направим, като използваме анотацията @EnableWebMvc .
4. Тестване на пролетния контекст
Започвайки с Spring 3.1, получаваме първокласна поддръжка за тестване за класове @Configuration :
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = {WebConfig.class, PersistenceConfig.class}, loader = AnnotationConfigContextLoader.class) public class SpringContextIntegrationTest { @Test public void contextLoads(){ // When } }
Посочваме класовете за конфигуриране на Java с анотацията @ContextConfiguration . Новият AnnotationConfigContextLoader зарежда дефинициите на боб от класовете @Configuration .
Забележете, че конфигурационният клас WebConfig не е бил включен в теста, тъй като трябва да се изпълнява в контекст на Servlet, което не е предоставено.
4.1. Използване на Spring Boot
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(...); } }
Можем да намерим подробна информация по този въпрос в нашата статия „Тестване в пролетно зареждане“.
5. Контролерът
В @RestController е централната артефакт в целия уеб Tier на 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); } }
Може би сте забелязали, че използвам ясна помощна програма RestPreconditions в стил Гуава :
public class RestPreconditions { public static T checkFound(T resource) { if (resource == null) { throw new MyResourceNotFoundException(); } return resource; } }
Внедряването на контролера не е публично - това е така, защото не е необходимо да бъде.
Обикновено контролерът е последният във веригата от зависимости. Той получава HTTP заявки от контролера Spring front ( DispatcherServlet ) и просто ги делегира напред към сервизен слой. Ако няма случай на употреба, при който контролерът трябва да бъде инжектиран или манипулиран чрез директна препратка, предпочитам да не го декларирам като публичен.
Съпоставянията на заявките са ясни. Както при всеки контролер, действителната стойност на картографирането, както и HTTP методът, определят целевия метод за заявката. @ RequestBody ще обвърже параметрите на метода с тялото на HTTP заявката, докато @ResponseBody прави същото за типа отговор и връщане.
В @RestController е краткият да включва както @ResponseBody и @Controller надписи, изписани с нашия клас .
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
Този урок илюстрира как да внедрите и конфигурирате REST услуга, използвайки Spring и Java-базирана конфигурация.
В следващите статии от поредицата ще се съсредоточа върху откриваемостта на API, усъвършенствано договаряне на съдържание и работа с допълнителни представяния на ресурс.
Целият код на тази статия е достъпен в Github. Това е проект, базиран на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.
ПОЧИВКА отдолу