SpringMvc in Action——使用SpringMvc创建REST API(不太懂)

数据为王

数据和软件相比,谁更重要?
如果你问问业务员,他们毫不犹豫的回答数据。
近几年以信息为中心的表述行状态转移(REST)已经替换传统SOAP Web服务的流行方案。SOAP一般会关注行为和处理,而REST关注的是要处理的数据。

了解REST

当谈论REST的时候,有一种常见的错误就是将其视为“基于URL的Web服务”——将REST作为另一种类型的远程过程调用(RPC)机制,就像SOAP一样,只不过是通过简单的HTTP URL来出发,而不是使用SOAP大量的XML命名空间。

其实,恰好相反。REST与RPC几乎没有任何关系,RPC是面向服务的,并关注于行为和动作;而REST是面向资源的,强调应用程序的事物和名词。

为了了解REST到底是什么,我们将它的名字拆分:

  • Representational(表述性):REST资源实际上可以用各种形式来进行表述,包括XML/JSON/HTML等等。
  • State(状态):当使用REST的时候,我们更关注资源的状态而不是对资源采取的行为。
  • Transfer(转移):REST涉及到转移资源数据,它以某种表述性形式从一个应用到另一个应用。

也就是说REST将资源的状态以最合适的形式(JSON或者XML等等)从服务器(客户端)到客户端(服务器)。

在REST中,资源通过URL进行识别和定位。至于RESTful URL的结构并没有严格的规则。但是URL应该能够识别资源,而不是简单的发一条命令到服务器上。再次强调,关注的核心是事物,而不是行为。
REST中会有行为,它们是通过HTTP方法来定义的。具体来讲,也就是GET、POST、PUT、DELETE、PATCH以及其他的HTTP方法构成了REST的动作。
这些HTTP方法匹配为如下的CRUD动作:

  • Create:POST
  • Read:GET
  • Update:PUT/PATCH
  • Delete:DELETE

通常来讲HTTP方法映射为CRUD动作,但是这并不严格。因为PUT方法可以用来创建新资源,POST方法也可以用来更新资源。基于对REST的这种观点,所以我尽量避免使用诸如REST服务、REST Web服务或类似的术语,这些术语会不恰当的强调行为。相反,我们应该更愿意强调REST面向资源的本质,并讨论RESTful资源。

Spring是如何支持REST的

  • 控制器可以处理所有的HTTP方法,包含四个主要的REST方法:GET/PUT/DELETE/POST,以及之后推出的PATCH。
  • 借助@PathVariable,控制器能够处理参数化的URL。
  • 借助Spring的视图和视图解析器,资源能够以多种方式进行表述,比如XML,JSON,Atom以及RSS的view实现。
  • 可以使用ContentNegotiatingViewResolver来选择最核实的客户端表述。
  • 借助@ResponseBody和各种HttpMethodConverter实现,能够替换基于视图的渲染方式。
  • 类似的,@RequestBody以及HttpMethodConverter实现可以将传入HTTP数据转化为传入控制器处理方法的Java对象。
  • 借助RestTempalte,Spring应用能够方便的使用REST资源。

创建第一个REST端点

借助Spring的支持来实现REST功能有一个很有利的地方,那就是我们已经掌握了很多创建RESTful控制器的知识。
首先,我们会在名为SpittleApiController的新控制器中创建第一个REST端点。
如下代码展现了这个新REST控制器的起始样子,它会提供Spittle资源。


@Controller
@RequestMapping("/spittles")
public class SpittleController {
    private static final String MAX_LONG_AS_STRING = "" + Long.MAX_VALUE;

    private SpittleRepository spittleRepository;

    @Autowired
    public SpittleController(SpittleRepository spittleRepository) {
        this.spittleRepository = spittleRepository;
    }

    @RequestMapping(method = RequestMethod.GET)
    public List<Spittle> spittles(
            @RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max,
            @RequestParam(value = "count", defaultValue = "20") int count
    ) {
        return spittleRepository.findSpittles(max, count);
    }
}

你可能会惊讶,这不就是我们之前写的代码么?并没有地方表明它是RESTful、服务于资源的控制器。我们回忆一下,当发起对“/spittles”的GET请求时,将会调用spittles()方法,它会查找并返回Spittle列表。而这个列表会通过注入的SpittleRepository获取到。列表又会被放入模型中,用于视图的渲染。对于基于浏览器的Web应用,这可能意味着模型数据会渲染到HTML页面中。
但是,我们现在讨论的是创建REST API。在这种情况下,HTML并不是合适的数据表述形式。

表述是REST中很重要的一个方面,它是关于客户端和服务器端针对某一资源是如何通信的。你可以使用JSON可以使用XML,甚至使用HTML。
为此,Spring提供了两种方法:

  • 内容协商(Content negotiation):选择一个视图,它能够将模型渲染为呈现给客户端的表述形式。
  • 消息转换器(Message Conversion):通过一个消息转换器所返回的对象转换为呈现给客户端的表述形式。

协商资源表述
当控制器的处理方法返回时,会有一个return "xxx"的视图名,如果方法不直接返回视图名,那么会根据请求的url判断得出。
在面向人类访问的Web应用程序中,通常选择的视图都会渲染为HTML。当要将视图名解析为能够产生资源表述的视图时,我们就有另外一个纬度需要考虑了。视图不仅要匹配视图名,而且所选择的视图要合适的客户端。如果客户端想要JSON,那么渲染HTML的视图就不行了。

Spring的ContentNegotiatingViewResolver是一个特殊的视图解析器,它考虑到了客户端所需要的内容类型。ContentNegotiatingViewResolver可以按照下述的形式就行配置:

@Bean
public ViewResolver cnViewResolver(){
	return new ContentNegotiatingViewResolver();
}

在这个简单的bean声明背后会涉及到很多事情。要理解ContentNegotiatingViewResolver如何工作,这设计到内容协商的两个步骤:

  1. 确定请求的媒体类型。
  2. 找到核实请求媒体类型的最佳视图。

让我们深入了解每个步骤来了解ContentNegotiatingViewResolver如何完成其任务的,首先从弄明白客户端需要什么类型的内容开始。

确定请求的媒体类型
表面上看,这可能是很简单的事,难道请求的Accept头部信息不是清楚的表明要发送什么样的表述给客户端了么?遗憾的是,Accept的头部信息并不总是可靠的。如果客户端是Web浏览器,那并不能保证客户端需要的类型就是浏览器在Accept头部所发送的值。Web浏览器一般只接受对人类用户友好的内容类型(text/html)。
ContentNegotiatingViewResolver会考虑到Accept头部信息,并使用它所请求的媒体类型,但是它会首先查看URL的文件拓展名,如果含有拓展名的话,ContentNegotiatingViewResolver会基于该拓展名确定所需类型。如果拓展名为json,那么所需内容必须是“application/json”,如果拓展名为".xml"的话,那么就是"application/xml",当然,html拓展名表明客户端所需的资源表述为HTML(text/html)。
如果识别不出,才会考虑Accept头部信息。再如果两者都没有,那么会使用“/”作为默认的内容类型。

影响媒体类型的选择
在上述的选择过程中,我们阐述了确定所请求媒体类型的默认策略。但是通过为其设置一个ContentNegotiatingManager,我们能改变它的默认行为,比如忽视请求的Accept头部信息等等。
一般而言我们使用XML配置ContentNegotiatingManager的话,那最有用的将会是ContentNegotiatingManagerFactoryBean。例如,我们可能希望在XML中配置ContentNegotiatingManager使用“applicaition/json”作为默认的内容类型。
而如果使用Java配置,最简单的方法就是扩展WebMvcConfigurerAdapter并重载configureContentNegotiation()方法:

idea重载快捷键ctrl+o (o表示override)

 @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
       configurer.defaultContentType(MediaType.APPLICATION_JSON);
    }

我们可以看到,这个方法参数中有一个ContentNegotiationConfigurer对象,ContentNegotiationConfigurer中的一些方法对应于ContentNegotiationManager的Setter方法,这样我们就能在ContentNegotiationManager创建时,设置任意内容协商相关的属性。我们调用defaultContentType()将默认的内容类型设置为"application/json"。
现在我们已经有了ContentNegotiationManager bean,接下来就需要将它注入到ContentNegotiatingViewResolver
的contentNegotiationManager属性中,这需要我们修改一下之前声明的ContentNegotiatingViewResolver的@Bean方法:

  
    @Bean
    public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {
      ContentNegotiatingViewResolver cnvr =
          new ContentNegotiatingViewResolver();
      cnvr.setContentNegotiationManager(cnm);
      return cnvr;
    }

这个@Bean方法注入了ContentNegotiationManager,并且使用它调用了setContentNegotiationManager()。这样的结果将会是ContentNegotiatingViewResolver将会使用ContentNegotiationManager所定义的行为。
配置ContentNegotionManager有很多细节,这里无法对其一一叙述。如下的程序清单是一个非常简单的配置样例,当我使用ContentNegotiatingViewResolver的时候,通常会使用这种方法:
它会默认使用HTML视图,但是对特定的视图名称会渲染为JSON输出:

  @Configuration
  public static class ContentNegotiationConfig extends WebMvcConfigurerAdapter {
    
    @Bean
    public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {
      ContentNegotiatingViewResolver cnvr =
          new ContentNegotiatingViewResolver();
      cnvr.setContentNegotiationManager(cnm);
      return cnvr;
    }

    @Override
    //默认为HTML
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
      configurer.defaultContentType(MediaType.TEXT_HTML);
    }
    
    @Bean
    //以bean的形式查找视图
    public ViewResolver beanNameViewResolver() {
       return new BeanNameViewResolver();
    }
    
    @Bean
    //将“spittles”定义为JSON视图
    public View spittles() {
      return new MappingJackson2JsonView();
    }
    
  }
  
}

使用HTTP信息转换器

暂略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值