使用Spring和Java Config构建REST API

使用Spring和Java Config构建REST API

翻译来源: Eugen Paraschiv 的 Build a REST API with Spring and Java Config

1.概述

本文介绍如何在Spring中设置REST - 控制器和HTTP响应代码,有效负载编组和内容协商的配置。

2.了解Spring中的REST

Spring框架支持两种创建RESTful服务的方法:

  • 将MVC与ModelAndView一起使用
  • 使用HTTP消息转换器

ModelAndView方法较旧且记录得更好,但也更冗长,配置更多。 它试图将REST范式转变为旧模型,这并非没有问题。 Spring团队理解这一点,并从Spring 3.0开始提供一流的REST支持。

基于HttpMessageConverter和注解的新方法,更加轻量级且易于实现。配置极少,它为您提供的RESTful服务提供了合理的默认设置。

3.Java 配置

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

新的@EnableWebMvc注解做了一些有用的事情 - 特别是在REST的情况下,它检测类路径上是否存在Jackson和JAXB 2,并自动创建和注册默认的JSON和XML转换器。 注解的功能等同于XML版本:

<mvc:annotation-driven />

这是一个捷径,尽管它在许多情况下可能有用,但它并不完美。 如果需要更复杂的配置,请删除注解并直接扩展WebMvcConfigurationSupport。

3.1 使用 Spring Boot

如果我们使用@SpringBootApplication注解并且spring-webmvc库位于类路径上,那么将使用默认自动配置自动添加@EnableWebMvc注释。

我们仍然可以通过,带有@Configuration注解并实现WebMvcConfigurer接口的类,来为此配置添加MVC功能。 我们还可以使用WebMvcRegistrationsAdapter实例来提供我们自己的RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver的实现。

最后,如果我们想要丢弃Spring Boot的MVC功能并声明自定义配置,我们可以使用@EnableWebMvc注解来实现。

4.测试Spring上下文

从Spring 3.1开始,我们获得了@Configuration类的一流测试支持:

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

我们使用@ContextConfiguration注解指定Java配置类。 新的AnnotationConfigContextLoader从@Configuration类加载bean定义。

请注意,Web Config配置类未包含在测试中,因为它需要在未提供的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(...);
    }
}

我们可以在“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实现是非public的 - 这是因为它不需要。

通常,控制器是依赖链中的最后一环。 它接收来自Spring前端控制器(DispatcherServlet)的HTTP请求,并简单地将它们委托给服务层。 如果没有必须通过直接引用注入或操纵控制器的用例,那么我不希望将其声明为public。

请求映射很直截了当。 与任何控制器一样,映射的实际值以及HTTP方法,确定请求的目标方法。 @RequestBody将方法的参数绑定到HTTP请求的主体,而@ResponseBody对响应和返回类型执行相同的操作。

@RestController是在我们的类中包含@ResponseBody和@Controller注解的简写。

它们还确保使用正确的HTTP转换器对资源进行编组和解组。 将进行内容协商以主要基于Accept标头选择将使用哪个活动转换器,尽管也可以使用其他HTTP标头来确定表示。

6.映射HTTP响应代码

HTTP响应的状态代码是REST服务中最重要的部分之一,主题可能很快变得非常复杂。 获得这些权利可以成为提供或破坏服务的原因。

6.1 未映射的请求

如果Spring MVC收到没有映射的请求,它会认为该请求不被允许,并返回405 METHOD NOT ALLOWED返回给客户端。

在将405返回给客户端时包含Allow HTTP头也是一种很好的做法,以指定允许哪些操作。 这是Spring MVC的标准行为,不需要任何其他配置。

6.2 有效的映射请求

对于任何具有映射的请求,Spring MVC认为请求有效,如果没有指定其他状态代码则以200 OK响应。

正因为如此,控制器为创建,更新和删除操作声明了不同的@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中的错误,从而提供更大的灵活性。

7.额外的Maven依赖项

除了标准Web应用程序所需的spring-webmvc依赖性之外,我们还需要为REST API设置内容编组和解组:

<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 使用Spring Boot

如果我们想要检索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.1.2.RELEASE</version>
</dependency>

Spring Boot默认使用Jackson。

如果我们想要以XML格式序列化我们的资源,我们必须将Jackson XML扩展(jackson-dataformat-xml)添加到我们的依赖项中,或者回退到JAXB实现(默认情况下在JDK中提供),方法是在我们资源上使用@XmlRootElement注解。

8.总结

本教程说明了如何使用Spring和基于Java的配置实现和配置REST服务。

在本系列的下一篇文章中,我将重点介绍API的可发现性,高级内容协商以及使用资源的其他表示。

Github上提供了本文的所有代码。 这是一个基于Maven的项目,因此它应该很容易导入和运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值