`

基于Spring MVC的Web应用开发(7) - Headers

 
阅读更多

本文接上一篇文章,介绍@RequestMapping中的headers属性,并进一步研究produces属性以及和它配对的consumes属性。

首先看看讲解用到的类:

package org.springframework.samples.mvc.simple;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SimpleControllerRevisited {

	@RequestMapping(value="/simple/revisited", method=RequestMethod.GET, headers="Accept=text/plain")
	public @ResponseBody String simple() {
		return "Hello world revisited!";
	}

}

header是HTTP协议中的一个概念,一般翻译成头域,为了不陷入讲解理论的泥潭,我们使用Firefox的firebug看一下header到底是什么,还记得HelloWorld那一篇文章的例子么?访问http://localhost:8080/web/simple,浏览器显示Hello World!,与本例唯一的区别就是@RequestMapping没有加headers, 打开firebug,在"网络"这一栏看到有一行记录URL "GET simple",Status "200 OK",Domain "localhost:8080"等信息,展开,有三列数据,分别为Headers,Response,HTML,看Headers这列,

Request Headers:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Connection: keep-alive
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2

 Response Headers:

Content-Length: 12
Content-Type: text/html
Server: Jetty(7.2.0.v20101020)

Request Headers中有个Accept,它是由浏览器发出的,表示浏览器认为它可以支持的格式,并不是真的只能支持这些格式,而Response Headers中有个Content-Type,这个值是服务端(如Servlet)封装到Respose的Headers中的。

访问例子程序的URL:http://localhost:8080/web/simple/revisited

Request Headers:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Connection: keep-alive
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2

 Response Headers:

Content-Length: 22
Content-Type: text/plain
Server: Jetty(7.2.0.v20101020)

Response Headers中的Content-Type变成了text/plain,因为RequestMapping的headers设置了Accept=text/plain。

注意这个Accept和Request Headers中的Accept没有半点关系,它跟Response Headers的Content-Type有关。

我们已经数次提及Content-Type了,其实@RequestMapping的headers属性除了Accept外也有Content-Type:

	@RequestMapping(value="/simple/revisited2", method=RequestMethod.GET, headers="Content-Type=text/plain")
	public @ResponseBody String simple2() {
		return "Hello world 2 revisited!";
	}

访问http://localhost:8080/web/simple/revisited2,发现浏览器上返回HTTP状态码为415的出错页面:

HTTP ERROR 415

Problem accessing /web/simple/revisited2. Reason:

    Unsupported Media Type

Powered by Jetty://

这是因为headers="Content-Type=text/plain"的含义是

它必须要求Request Headers里的Content-Type为"text/plain"才能执行该方法,

看firebug的Request Headers:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Connection: keep-alive
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2

没有Content-Type属性,不光这个,前面的那个Request Headers也没有Content-Type,其实GET方式访问的URL的Request Header里压根就没有Content-Type(我原来也不知道,直到我搜索到了这篇文章)! 所以我们只能使用POST方式测试这个特性,很遗憾,我暂时没有找到如何通过浏览器地址栏的方式来POST提交,但是,想到了上一篇文章附录中的RestTemplate,可以写个Java小程序模拟一下,在SimpleControllerRevisited中增加一个方法:

	@RequestMapping(value="/simple/revisited3", method=RequestMethod.POST, headers="Content-Type=text/plain")
	public @ResponseBody String simple3() {
		return "Hello world 3 revisited!";
	}

写个RestTemplate的小程序:

package org.springframework.samples.mvc.simple;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class SimpleControllerRevisitedTest {

	public static void main(String[] args) {
		testPost();
	}
	
	public static void testPost() {
		RestTemplate template = new RestTemplate();
		ResponseEntity<String> entity = template.postForEntity(
				"http://localhost:8080/web/simple/revisited3", null, String.class);
		String body = entity.getBody();
		MediaType contentType = entity.getHeaders().getContentType();
		System.out.println("contentType:[" + contentType + "]");
		HttpStatus statusCode = entity.getStatusCode();
		System.out.println("statusCode:[" + statusCode + "]");
	}
}

 执行,报错:

DEBUG: org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/web/simple/revisited3"
DEBUG: org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, */*]
WARN : org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/web/simple/revisited3" resulted in 415 (Unsupported Media Type); invoking error handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:76)
	at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:486)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:443)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:302)
	at org.springframework.samples.mvc.simple.SimpleControllerRevisitedTest.testPost(SimpleControllerRevisitedTest.java:16)
	at org.springframework.samples.mvc.simple.SimpleControllerRevisitedTest.main(SimpleControllerRevisitedTest.java:11) 

还是报415 Unsupported Media Type,

这说明Content-Type并不是text-plain,那么到底是什么呢?新写一个方法,去掉headers属性,仅保留POST:

	@RequestMapping(value="/simple/revisited4", method=RequestMethod.POST)
	public @ResponseBody String simple4() {
		return "Hello world 4 revisited!";
	}

 将SimpleControllerRevisitedTest中的URL改为http://localhost:8080/web/simple/revisited4, 查看服务端打印的日志(注意访问前一个URL时,服务端是不打印此日志的,因为Request Header中的Content-Type就不满足要求,直接拒掉了):

DEBUG: org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor - Reading [[B] as "application/x-www-form-urlencoded" using [org.springframework.http.converter.ByteArrayHttpMessageConverter@1d05c9a1]

发现使用POST方式,Request Headers中的Content-Type为"application/x-www-form-urlencoded",

那么再增加一个方法:

	@RequestMapping(value="/simple/revisited5", method=RequestMethod.POST, headers="Content-Type=application/x-www-form-urlencoded")
	public @ResponseBody String simple5() {
		return "Hello world 5 revisited!";
	}

 将SimpleControllerRevisitedTest中的URL改为http://localhost:8080/web/simple/revisited5

DEBUG: org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/web/simple/revisited5"
DEBUG: org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, */*]
DEBUG: org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/web/simple/revisited5" resulted in 200 (OK)
DEBUG: org.springframework.web.client.RestTemplate - Reading [java.lang.String] as "text/plain;charset=ISO-8859-1" using [org.springframework.http.converter.StringHttpMessageConverter@5f326484]
contentType:[text/plain;charset=ISO-8859-1]
statusCode:[200]

从程序运行结果看,一切OK(注意不要被这个日志中的contentType弄混了,这个contentType是Response的Headers里的)。

总结一下,@RequestMapping中的headers有两种写法,

一种是:headers="Accept=text/plain",

另一种是:headers="Content-Type=application/x-www-form-urlencoded",

Accept指定的格式为Response Headers的Content-Type的格式,

Content-Type指定的格式为Reqeust Headers必须要满足的格式,

否则将被SpringMVC直接拒绝掉,并返回415的HTTP状态码。

大家可能会想,@RequestMapping的headers属性搞这么复杂干嘛?Spring团队应该是听到了社区群众的呼声,或者他们自己都觉得晕?因此在Spring 3.1 M2发布的时候,他们做了改进,SpringSource的Team Blog上有一篇文章 SPRING 3.1 M2: SPRING MVC ENHANCEMENTS,说道了Spring 3.1 M2版本针对以前版本的编程模型改进(programming model improvement),将第四点和第五点简而言之就是:

@RequestMapping中

headers="Content-Type=application/json"被consumes="application/json"替换了,

headers="Accept=application/json"被produces="application/json"替换了。

现在应该对上一篇文章中,我写道

后者通过指定@RequestMapping的produces属性为text/plain,字符集为UTF-8,可以避免乱码,这个是可以理解的。

 理解了,任何一个框架都是不断的在发展和完善的,如果能在学习的过程中,知道一点框架的变迁史,将会对我们的理解产生莫大的好处。

 

 

 

 

 

 

分享到:
评论
3 楼 hongxingyaru_d5b032 2017-10-13  
但是其中“这个Accept和Request Headers中的Accept没有半点关系”说法是有问题的。浏览器包装的request中包含了“*/*”,匹配所有,所以mapping中的headers=‘accept=text/html’没起作用,可以尝试去掉“*/*”,headers=‘accept=text/html’就会有效。看后一点拙见。
2 楼 hongxingyaru_d5b032 2017-10-13  
学习到了,感谢分享!
1 楼 bo_hai 2016-10-18  
总结的不错。

相关推荐

    Spring MVC 文件上传下载 后端 - Java.zip

    在Java Web开发中,Spring MVC框架是一个非常流行的用于构建企业级应用的模型-视图-控制器(MVC)架构。这个压缩包“Spring MVC 文件上传下载 后端 - Java.zip”很可能包含了关于如何在Spring MVC中实现文件上传和...

    Mastering Spring Webmvc

    Spring Web MVC是Spring框架的核心组件之一,它为构建基于Java的Web应用程序提供了模型-视图-控制器(MVC)架构。本篇将深入探讨Spring Web MVC的重要概念、工作原理以及实际应用。 一、Spring MVC概述 Spring MVC...

    Spring MVC实现文件的上传下载

    Spring MVC 是一个强大的Java Web开发框架,用于构建可维护、高性能和灵活的Web应用程序。它在Spring框架的基础上,为处理HTTP请求提供了模型-视图-控制器(MVC)架构。在Spring MVC中实现文件的上传和下载是常见的...

    spring第11章--ajax,上传下载,拦截器.rar_spring mvc_spring 下载_spring 使用ajax

    在Spring MVC框架中,Ajax(Asynchronous JavaScript and XML)是一种常用的技术,用于在不刷新整个页面的情况下与服务器进行异步通信。本章将探讨如何在Spring MVC中集成和使用Ajax,以及涉及的上传和下载功能,...

    spring4mvc

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,主要用于构建 MVC(Model-View-Controller)模式的 Web 应用程序。在本教程中,我们将深入探讨 Spring MVC 的核心概念、配置以及如何...

    Spring MVC_快速入门分析篇

    Spring MVC 是一个基于 Java 的模型-视图-控制器(MVC)架构,是 Spring 框架的一部分,用于构建Web应用程序。它提供了丰富的功能,帮助开发者处理HTTP请求、数据绑定、视图渲染等任务,使得开发过程更加简洁高效。...

    spring mvc

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在Spring MVC中,开发者可以利用模型-视图-控制器(MVC)架构模式来分离业务逻辑、数据处理和...

    spring mvc文件上传下载实例

    在Spring MVC框架中,文件上传和下载是常见的功能需求,特别是在构建Web应用程序时。这篇博客“spring mvc文件上传下载实例”将引导我们如何在Spring MVC项目中实现这两个功能。 首先,我们需要理解Spring MVC的...

    spring mvc上传 下载

    在IT行业中,Spring MVC是一个广泛使用的Java框架,用于构建基于模型-视图-控制器(MVC)模式的Web应用程序。本文将深入探讨Spring MVC在文件上传和下载方面的应用,以及如何结合FineUploader和jQuery实现这一功能。...

    8-Spring-mvc-文件上传1

    在Spring MVC框架中,文件上传是一项常见的功能,它允许用户通过Web表单上传文件到服务器。Spring MVC通过集成Apache Commons FileUpload库实现了这一功能。在本文中,我们将深入探讨如何配置和使用Spring MVC进行...

    spring mvc +Extjs

    在现代Web开发中,Spring MVC和ExtJS是两个常见的技术,用于构建强大的后端和前端应用。本篇文章将深入探讨如何使用Spring MVC 3与ExtJS进行数据交互,特别是通过JSON格式来实现这一过程。 首先,Spring MVC 3引入...

    Spring-MVC的文件上传,下载的技术攻克

    Spring MVC 是一个强大的Java Web开发框架,用于构建高效、可维护的Web应用程序。在这个主题中,我们将深入探讨Spring MVC如何支持文件的上传和下载功能,这对于任何交互式Web应用都是必不可少的部分。 首先,让...

    Spring MVC数据校验.docx

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建Web应用程序提供了一种模型-视图-控制器(MVC)的编程模型。在Spring MVC中,DispatcherServlet扮演着核心角色,它作为整个Web应用的前端控制器,负责接收...

    Spring MVC--2.@RequestMapping 映射请求

    在实际开发中,通常会结合使用`@RequestMapping`和其他注解,如`@ControllerAdvice`(全局异常处理)、`@ResponseBody`(将方法返回值直接转换为HTTP响应体)等,构建出功能丰富的Spring MVC应用程序。 通过理解并...

    Spring MVC文件上传下载

    Spring MVC 是一个强大的Java web开发框架,用于构建可维护、模块化且松散耦合的Web应用程序。在Spring MVC中,文件的上传和下载是常见的功能需求,这涉及到客户端与服务器之间的数据传输。本篇文章将深入探讨Spring...

    springboot 跨域请求

    在`pom.xml`文件中添加Spring Web依赖,因为CORS功能是基于Spring MVC的: ```xml &lt;groupId&gt;org.springframework.boot &lt;artifactId&gt;spring-boot-starter-web ``` 2. 配置CORS: 可以通过两种方式配置...

    使用Rest Assured和Mockmvc进行Spring mvc集成测试.zip

    在Spring MVC框架中,集成测试是确保应用程序各个组件协同工作的关键步骤。`Rest Assured`和`MockMvc`是两种常用的工具,它们可以帮助开发者高效地完成这个任务。本篇文章将详细探讨如何使用这两个库进行Spring MVC...

    Spring MVC upload/download file(注释和非注释实现)

    在IT行业中,Spring MVC是一个广泛使用的Java框架,用于构建Web应用程序。它提供了处理HTTP请求、渲染视图以及处理用户输入的强大功能。在这个场景下,我们关注的是Spring MVC如何实现文件的上传与下载功能,这对于...

    6_构建一个简单的spring-boot的web项目实现上传和下载文件.zip

    Spring Boot以其简洁的配置和强大的功能集深受开发者喜爱,它简化了开发过程,特别是对于构建RESTful API和Web服务。以下是你需要了解的关键知识点: 1. **Spring Boot基础知识**: - Spring Boot是Spring框架的...

Global site tag (gtag.js) - Google Analytics