JavaEE程序设计【十二】

三、Json数据交互和Restful支持


1、什么是JSON?

  • JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛。
  • 采用完全独立于编程语言的文本格式来存储和表示数据。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

在 JavaScript 语言中,一切都是对象。因此,任何JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:

  • 对象表示为键值对,数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

==注意:如果使用JSON存储单个数据(如”abc”),一定要使用数组的形式,不要使用 Object形式,因为 Object形式必须是”名称:值”的形式.==

JSON 键值对是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 “” 包裹,使用冒号 : 分隔,然后紧接着值:

1
2
3
{"name": "QinJiang"}
{"age": "3"}
{"sex": "男"}

很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:

JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。

1
2
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串

JSON 和 JavaScript 对象互转

要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:

1
2
var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//结果是 {a: 'Hello', b: 'World'}

要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:

1
2
var json = JSON.stringify({a: 'Hello', b: 'World'});
//结果是 '{"a": "Hello", "b": "World"}'

2、Spring的json数据交互

Spring提供了一个 HttpMessage Converter<T>接口来实现浏览器与控制器类( Controller)之间的数据交互.

MappingJackson2HttpMessageConverter:SpringMVC默认处理JSON格式请求响应的实现类。

可以将Java对象转换为JSON对象和XML对象

也可以将JSON对象和XML对象转换为Java对象

3、注解开发

只需要配置<mvc:annotation-driven />

使用@RequestBody和@ResponseBody

使用实例:

1
2
3
4
5
6
@RequestMapping("/testJson")
@ResponseBody
public User testJson(@RequestBody User user) {
System.out.println(user);
return user;
}

外部JSON—>@RequestBody—>转化为user —>return user—>@ResponseBody—>转化为JSON

4、显示开发(很少使用)

5、静态资源处理

三种方法:

  • 配置<mvc:resources location="" mapping="" />
  • (推荐)使用<mvc:default-servlet-handler/>
  • (不推荐)配置web.XML使用Tomcat(或者其他web容器)实现

Spring MVC静态资源处理:<mvc:resources />

1
<mvc:resources location="/,classpath:/META-INF/publicResources/" mapping="/resources/**"/>

以上配置将Web根路径”/“及类路径下 /META-INF/publicResources/ 的目录映射为/resources路径。假设Web根路径下拥有images、js这两个资源目录,在images下面有bg.gif图片,在js下面有test.js文件,则可以通过 /resources/images/bg.gif 和 /resources/js/test.js 访问这二个静态资源。

假设WebRoot还拥有images/bg1.gif 及 js/test1.js,则也可以在网页中通过 /resources/images/bg1.gif 及 /resources/js/test1.js 进行引用。

6、RESTful支持

==简单来说,RESTful风格就是把请求参数变成请求路径的一种风格。==

Web应用程序最重要的REST原则是,客户端和服务器之间的交互在请求之间是无状态的

从客户端到服务器的每个请求都必须包含理解请求所必需的信息.

例如:购买商品,你给出商品信息,服务器给你小票,之前和之后不会再保留任何相关信息。

动词也变成了名词

==主要是使用@PathVariable==

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("/user/{id}")
@ResponseBody
public User findById(@PathVariable("id") String id) {
System.out.println(id);
if("1".equals(id)) {
User user=new User();
user.setId(1);
user.setUsername("123");
user.setPassword("aa");
return user;
}
return null;
}

四、拦截器和文件上传下载


1、拦截器

(1)什么是拦截器?

Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。

例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。

拦截器与过滤器的比较

相似

  • 都有优先处理请求的权利,都可以决定是否将请求转移到请求的实际处理的控制器处。
  • 都可以对请求或者会话当中的数据进行加工。

不同

  • 拦截器可以做前置处理也可以做后置处理,还可以进行完成处理,控制的 更加细致,而过滤器只负责前面的过滤行为而已。
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
  • 过滤器优先执行,还是拦截器优先呢?———-过滤器优先。
  • 过滤器是servlet规范里面的组件。任何java web工程都可以使用
  • 拦截器都是框架自己而外添加的组件。

==三种拦截器的执行时间段:==

preHandle:在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作。

postHandle:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。

afterCompletion:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

(2)如何实现?

1、通常拦截器类可以通过两种方式来定义。

  • 通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
  • 通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

2、在Spring MVC的配置文件中进行配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<mvc:interceptors>
<bean class="cn.edu.ccc.interceptor.CustomInterceptor"/>
<!-- 全局拦截器,拦截所有请求 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- /**配置,表示拦截所有路径 -->
<mvc:exclude-mapping path=""/>
<!-- 配置不需要拦截的路径 -->
<bean class=" cn.edu.ccc.interceptor.Interceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<!-- /hello表示拦截所有以“/hello”结尾的路径 -->
<bean class=" cn.edu.ccc.interceptor.Interceptor2" />
</mvc:interceptor>
...
</mvc:interceptors>

注意<mvc:interceptor>中的子元素必须按照上述代码的配置顺序进行编写,否则文件会报错。

(3)单个和多个拦截器执行的流程

==多个:pre顺序执行;post和after反序执行==

==注意:顺序是按照XML中配置的顺序。正序就是XML中的顺序,逆序就是XML中的反顺序==

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 配置拦截器 -->
<mvc:interceptors>

<!-- 拦截器2 -->
<mvc:interceptor>
<mvc:mapping path="/hello" />
<bean class="cn.edu.ccc.ch15.interceptor.Interceptor2" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截的 -->
<bean class="cn.edu.ccc.ch15.interceptor.Interceptor1" />
</mvc:interceptor>
<!--使用bean直接定义在<mvc:interceptors>下面的拦截器将拦截所有请求 -->
<bean class="cn.edu.ccc.ch15.interceptor.CustomInterceptor" />

</mvc:interceptors>

(4)应用:常用来登录验证

若访问本站点下除了登录页面的其他页面则进行拦截判断是否登录。登录则继续访问否则转发到登录页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 获取请求的URL
String url = request.getRequestURI();
System.out.println("uri:" + url);
// URL:除了login.jsp是可以公开访问的,其它的URL都进行拦截控制
if (url.indexOf("/login") >= 0) {
return true;
}
// 获取Session
HttpSession session = request.getSession();
User user = (User) session.getAttribute("USER_SESSION");
// 判断Session中是否有用户数据,如果有,则返回true,继续向下执行
if (user != null) {
return true;
}
// 不符合条件的给出提示信息,并转发到登录页面
request.setAttribute("msg", "您还没有登录,请先登录!");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
1
2
3
4
5
6
7
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.edu.ujn.ch15.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

2、文件上传和下载

(1)上传

==1、前端配置==

多数文件上传都是通过表单形式提交给后台服务器的,因此,要实现文件上传功能,就需要提供一个文件上传的表单,而该表单必须满足以下3个条件

  • form表单的method属性设置为post;
  • form表单的enctype属性设置为multipart/form-data
  • 提供<input type="file" name="filename" />的文件上传输入框。
    • multiple属性是HTML5中新属性,可实现多文件上传。
1
2
3
<form action="uploadUrl" method="post" enctype="multipart/form-data">
<input type="file" name="filename" multiple="multiple" />
<input type="submit" value="文件上传" />
  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

==2、导入jar包==

1
2
3
4
5
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>

==3、MultipartResolver配置==

当form表单的enctype属性为multipart/form-data时,浏览器就会采用二进制流来处理表单数据,服务器端就会对文件上传的请求进行解析处理。Spring MVC通过MultipartResolver实现文件上传功能。MultipartResolver是一个接口对象,需要通过它的实现类CommonsMultipartResolver来完成文件上传工作。

1
2
3
4
5
6
7
8
<bean id="multipartResolver"          	     
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8" />
<!-- 设置请求编码格式,必须与JSP中的pageEncoding属性一致,默认为ISO-8859-1 -->
<property name="maxUploadSize" value="2097152" />
<!-- 设置允许上传文件的最大值(2M),单位为字节 -->
...
</bean>
  • maxUploadSize:上传文件最大长度(以字节为单位);

  • maxInMemorySize:缓存中的最大尺寸;

  • defaultEncoding:默认编码格式;

  • resolveLazily:推迟文件解析,以便在Controller中捕获文件大小异常。

    注意因为MultipartResolver接口的实现类CommonsMultipartResolver内部 是引用multipartResolver字符串获取该实现类对象并完成文件解析的,所以在配置CommonsMultipartResolver时必须指定该Bean的id为multipartResolver

==4、使用MultipartFile接受前段传来的数据流,使用其相应方法对数据进行操作==`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@RequestMapping("/file")
public String toUpload() {
return "fileUpload";//这里跳转的是jsp
}
@RequestMapping("/fileUpload")
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("uploadfile") List<MultipartFile> uploadfile,
HttpServletRequest request) {
// 判断所上传文件是否存在
if (!uploadfile.isEmpty() && uploadfile.size() > 0) {
//循环输出上传的文件
for (MultipartFile file : uploadfile) {
// 获取上传文件的原始名称
String originalFilename = file.getOriginalFilename();
// 设置上传文件的保存地址目录
String dirPath =
request.getServletContext().getRealPath("/upload/");
File filePath = new File(dirPath);
// 如果保存文件的地址不存在,就先创建目录
if (!filePath.exists()) {
filePath.mkdirs();
}
// 使用UUID重新命名上传的文件名称(上传人_uuid_原始文件名称)
String newFilename = name+ "_"+UUID.randomUUID() +
"_"+originalFilename;
try {
// 使用MultipartFile接口的方法完成文件上传到指定位置
file.transferTo(new File(dirPath + newFilename));
System.out.println(dirPath);
} catch (Exception e) {
e.printStackTrace();
return"error";
}
}
// 跳转到成功页面
return "success";
}else{
return"error";
}
}

(2)下载

==1、前端设置==

在客户端页面使用一个文件下载的超链接,该链接的href属性要指定后台文件下载的方法以及文件名(需要先在文件下载目录中添加了一个名称为“1.jpg”的文件)。

前端JSPdownload.jsp

<a href="${pageContext.request.contextPath }/download?filename=<%=URLEncoder.encode("壁纸.jpg", "UTF-8")%>">中文名称文件下载 </a>

==2、后端使用ResponseEntity处理==

在后台使用Spring MVC提供的ResponseEntity类型对象完成文件下载,使用它可以很方便的定义返回的HttpHeaders对象和HttpStatus对象,通过对这两个对象的设置,即可完成下载文件时所需的配置信息。

文件下载中的ResponseEntity对象有些类似前面章节中的@ResponseBody注解,它用于直接返回结果对象。

响应头信息中的MediaType代表的是Interner Media Type(即互联网媒体类型),也叫做MIME类型,MediaType.APPLICATION_OCTET_STREAM的值为application/octet-stream,即表示以二进制流的形式下载数据;

HttpStatus类型代表的是Http协议中的状态,示例中的HttpStatus.OK表示200,即服务器已成功处理了请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@RequestMapping("/down")
public String toDownload() {
return "download";//这里跳转的是jsp
}

@RequestMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,
String filename) throws Exception{
// 指定要下载的文件所在路径
String path = request.getServletContext().getRealPath("/upload/");
// 创建该文件对象
File file = new File(path+File.separator+filename);
// 对文件名编码,防止中文文件乱码
filename = this.getFilename(request, filename);
// 设置响应头
HttpHeaders headers = new HttpHeaders();
// 通知浏览器以下载的方式打开文件
headers.setContentDispositionFormData("attachment", filename);
// 定义以流的形式下载返回文件数据
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 使用Sring MVC框架的ResponseEntity对象封装返回下载数据
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),
headers,HttpStatus.OK);
}
/**
* 根据浏览器的不同进行编码设置,返回编码后的文件名
*/
public String getFilename(HttpServletRequest request,
String filename) throws Exception {
// IE不同版本User-Agent中出现的关键词
String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
// 获取请求头代理信息
String userAgent = request.getHeader("User-Agent");
for (String keyWord : IEBrowserKeyWords) {
if (userAgent.contains(keyWord)) {
//IE内核浏览器,统一为UTF-8编码显示
return URLEncoder.encode(filename, "UTF-8");
}
}
//火狐等其它浏览器统一为ISO-8859-1编码显示
return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}

==3、中文名文件乱码问题解决==

为了解决浏览器中文件下载时中文名称的乱码问题,可以在前端页面发送请求前先对中文名进行统一编码,然后在后台控制器类中对文件名称进行相应的转码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RequestMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,
String filename) throws Exception{
// 指定要下载的文件所在路径
String path = request.getServletContext().getRealPath("/upload/");
// 创建该文件对象
File file = new File(path+File.separator+filename);
// 对文件名编码,防止中文文件乱码
filename = this.getFilename(request, filename);
// 设置响应头
HttpHeaders headers = new HttpHeaders();
// 通知浏览器以下载的方式打开文件
headers.setContentDispositionFormData("attachment", filename);
// 定义以流的形式下载返回文件数据
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 使用Sring MVC框架的ResponseEntity对象封装返回下载数据
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),
headers,HttpStatus.OK);
}
/**
* 根据浏览器的不同进行编码设置,返回编码后的文件名
*/
public String getFilename(HttpServletRequest request,
String filename) throws Exception {
// IE不同版本User-Agent中出现的关键词
String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
// 获取请求头代理信息
String userAgent = request.getHeader("User-Agent");
for (String keyWord : IEBrowserKeyWords) {
if (userAgent.contains(keyWord)) {
//IE内核浏览器,统一为UTF-8编码显示
return URLEncoder.encode(filename, "UTF-8");
}
}
//火狐等其它浏览器统一为ISO-8859-1编码显示
return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}
-----------------------本文结束 感谢阅读-----------------------
坚持原创技术分享,您的支持将鼓励我继续创作!恰饭^.^~