@RestController // 此处换成了@RestController,而不是@Controller了
public class DemoController {
// 下面所有方法都不写@ResponseBody注解
@RequestMapping("/demo")
public Map demo(){
HashMap map = new HashMap<>();
map.put("name","张三");
map.put("age",18);
return map;
}
@RequestMapping("/demo1")
public People demo1(){
People peo = new People();
peo.setId(1);
peo.setName("张三");
return peo;
}
@RequestMapping("/demo2")
public List demo2(){
List list = new ArrayList<>();
list.add(new People(1,"张三"));
list.add(new People(2,"李四"));
return list;
}
@RequestMapping("/demo3")
public List
Rest(Representational State Transfer:表现层状态转移)是一种软件架构风格,其核心是面向资源的一种设计。何为面向资源,意思是网络上的所有事物都可以抽象为资源,而每个资源都有唯一的资源标识,对资源的操作不应该改变这些标识。
通俗讲就是每个资源都有一个url地址,而不是不同的操作有不同的url地址,比如我们对用户信息的增删改查,用户就是资源,增删改查是操作,以前我们是一个操作一个url地址,现在按照Restful的说法,url地址只能有一个。
restful中要求使用不同的请求方式来标记对资源的操作方式,达到调用不同的后台功能方法来处理请求的目的。
Title
请求数据被嵌套在了请求地址中,不能在像以前直接在单元方法上声明形参来接收了,需要结合@PathVariable注解来获取。
/**
* @RequestMapping注解可以接收任意请求方式的请求
* @GetMapping("地址"):接收GET请求,一般用在查询方法上
* @DeleteMapping("地址"):接收DELETE请求,一般用在删除方法上
* @PostMapping("地址"):接收POST请求,一般用户在新增上
* @PutMapping("地址"):接收PUT请求,一般用在修改上
*/
//查询用户信息
@GetMapping("/user/{id}")
public String selUser(@PathVariable Integer id){
System.out.println("用户ID为:"+id);
return "success.jsp";
}
//删除用户信息
@DeleteMapping("/user/{id}")
public String delUser(@PathVariable Integer id){
System.out.println("用户ID为:"+id);
return "success.jsp";
}
//新增用户信息
@PostMapping("/user/{id}/{name}/{age}")
public String addUser(@PathVariable Integer id,@PathVariable String name,@PathVariable Integer age){
System.out.println("id = " + id + ", name = " + name + ", age = " + age);
return "success.jsp";
}
//修改用户信息
@PutMapping("/user/{id}/{name}")
public String updateUser(@PathVariable Integer id,@PathVariable String name){
System.out.println("id = " + id + ", name = " + name);
return "success.jsp";
}
我们知道,为了提高安全性,可以把页面放入到WEB-INF中。但是放入到WEB-INF中之后,访问页面之前必须先执行控制器,可以使用Restful方式显示页面,这样可以大大减少显示页面的控制器数量。
/*
其中映射路径前面的/page/不是强制要求,随意在前面添加的内容。
也可以不写/page/,也可以多加几级路径。
*/
@RequestMapping("/page/{yemian}")
public String showPage(@PathVariable String yemian){
return "/WEB-INF/page/"+yemian+".jsp";
}
@ResponseBody注解是类或方法级注解。 默认情况下控制单元只能返回String类型的数据。返回其他数据类型出现406状态码。@ResponseBody注解可以对返回值进行设置。
当方法上添加@ResponseBody注解后,控制单元方法返回值将不再被视图解析器进行解析|不会使用转发。而是把返回值放入到响应流中进行响应。等效于直接 使用PrintWriter对象进行打印。
@RequestMapping("/demo1")
@ResponseBody
public String demo1(){
return "bjsxt";
}return "bjsxt";}
访问控制器后的效果是在浏览器直接打印bjsxt字符串。
- 在使用@ResponseBody注解时,只要返回值类型不是 类 或 Map 或 List 等满足 键值对 类型。Spring MVC 都会设置响应内容类型为text/html;charset=ISO-8859-1。
- text/html;charset=ISO-8859-1中编码是不支持中文的。所以返回值中包含中文,打印在浏览器中会出现乱码。
- 配合@RequestMapping(produces = "text/plain;charset=utf-8")设置响应内容类型及编码格式。
@RequestMapping(value="/demo1",produces = "text/html;charset=utf-8")
@ResponseBody
public String demo1() {
return "北京尚学堂";
}
@ResponseBody注解可以把控制单元返回值自动转换为JSON字符串。主要完成下面几个事情:
(1)判断返回值是否为JavaBean(实体类)、JavaBean数组、List
、 Map、List 等 满足键值对的类型。 (2)如果满足键值对类型,会使用Jackson(需要Jackson依赖)把对象转换为JSON字符 串,设置到响应流中。
(3)同时会设置响应内容类型(Content-Type)为application/json;charset=utf-8。
(4)因为Spring MVC默认使用Jackson作为JSON转换工具,所以必须保证项目中存在 Jackso的依赖。
@RequestMapping("/demo")
@ResponseBody // 千万不要忘记写了
public Map demo(){
HashMap map = new HashMap<>();
map.put("name","张三");
map.put("age",18);
return map;
}
在浏览器访问后会在浏览器打印内容:
查看谷歌浏览器的开发者工具,可以看到响应内容类型被设置为application/json。
在Spring MVC中支持把返回值转换为XML文件。如果还是使用jackson-databind依赖,默认只能转换返回值为类类型的控制单元,返回值为List是无法转换为XML的,同时还要求实体类上必须有@XmlRootElement,才能转换。
如果项目中所有控制单元返回值结果都希望是XML格式,可以按照下面步骤完成。
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.9.9
控制单元方法和转换为JSON时写法完全相同。
@RequestMapping("/demo")
@ResponseBody // 前往不要忘记写了
public Map demo(){
HashMap map = new HashMap<>();
map.put("name","张三");
map.put("age",18);
return map;
}
@RequestMapping("/demo1")
@ResponseBody
public People demo1(){
People peo = new People();
peo.setId(1);
peo.setName("张三");
return peo;
}
@RequestMapping("/demo2")
@ResponseBody
public List demo2(){
List list = new ArrayList<>();
list.add(new People(1,"张三"));
list.add(new People(2,"李四"));
return list;
}
@RequestMapping("/demo3")
@ResponseBody
public List> demo3(){
List> list = new ArrayList<>();
Map map = new HashMap<>();
map.put("id","1");
map.put("name","张三");
list.add(map);
return list;
}
客户端向服务端发送的请求都是异步Ajax(或类似Ajax的异步请求)。对于这样的项目,控制器中所有的方法都包含@ResponseBody注解。
类似下面效果:
- 这种类中所有的控制单元方法都有@ResponseBody,可以使用@RestController进行简化。
- 当类上使用的是@RestController而不是@Controller时,控制单元方法不再需要写@ResponseBody也不需要写@Controller,Spring MVC在解析控制单元方法时会自动带有@ResponseBody注解。
- 一旦类上使用了@RestController,所有控制单元返回都是XML或JSON数据。而无法实现页面跳转功能了。
@RestController // 此处换成了@RestController,而不是@Controller了
public class DemoController {
// 下面所有方法都不写@ResponseBody注解
@RequestMapping("/demo")
public Map demo(){
HashMap map = new HashMap<>();
map.put("name","张三");
map.put("age",18);
return map;
}
@RequestMapping("/demo1")
public People demo1(){
People peo = new People();
peo.setId(1);
peo.setName("张三");
return peo;
}
@RequestMapping("/demo2")
public List demo2(){
List list = new ArrayList<>();
list.add(new People(1,"张三"));
list.add(new People(2,"李四"));
return list;
}
@RequestMapping("/demo3")
public List> demo3(){
List> list = new ArrayList<>();
Map map = new HashMap<>();
map.put("id","1");
map.put("name","张三");
list.add(map);
return list;
}
}
- @RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List
等类型 。- 既然是转换为请求体数据,所以不能是GET类型请求(GET没有请求体),多用在POST类型的请求中。
- 如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML。
在客户端中无论使用的是
表单,还是Ajax请求,post请求内容类型都是application/x-www-form-urlencoded,表示普通表单参数。
@RequestMapping("/testContentType")
@ResponseBody // 客户端希望要JSON数据,所以一定要有这个注解
public People testContentType(People peo) {
System.out.println(peo);
return peo;
}
- 如果希望修改请求内容类型,可以使用HTML的
中enctype属性或使用Ajax中contentType属性进行设置。
的enctype属性一般只有在文件上传时才会修改,所以希望传递特定类型请求参数内容时,都是通过Ajax进行请求。
下面演示下,请求参数内容为JSON字符串的写法。
$.ajax({
url:"testContentType",
contentType:"application/json",// 修改请求内容类型为JSON
data:'{"id":1,"name":"张三"}',// 传多个数据必须有单引号,没有单引号无效
type:"post",// 不能是GET类型请求
success:function (data) {
console.log(data);
},
dataType:"json"
});
@RequestMapping("/testContentType")
@ResponseBody
public People testContentType(@RequestBody People peo) {
System.out.println(peo);
return peo;
}
传递json字符串数据有三次需要重点注意的地方:
- contentType:必须设置。如果没有设置这个属性,取值默认是application/x-www-form-urlencoded,表示普通表单参数。当设置为"application/json"时,会把data取值设置到请求体中,所以服务端接收参数时就不能按照普通表单参数进行接收。
- data:请求参数。必须是JSON字符串类型,不能是JSON格式的对象。因为在JSON中key两侧必须有双引号,所以data传多个数据必须有单引号包含。向后端传输多个json字符串格式的数据,没有单引号无效。
- type:请求类型不能是get类型,因为get类型没有请求体。
'{"id":1,"name":"张三"}' json字符串
{"id":1,"name":"张三"} json对象
因为一个请求只有一个请求体。控制单元参数中绝对不允许出现两个@RequestBody注解。
因为@RequestBody底层使用Jackson,所以只适用于把请求体数据转换为JavaBean或Map。绝对不能在@RequestBody后面使用String等类型接收请求体内容。
- 请求方式为post请求,使用请求体完成文件的传输。
- enctype:multipart/form-data。
- 引入Commons-Fileupload依赖,CommonsMultipartResovler对Commons-Fileupload进行了封装。
- 配置文件解析器,MultipartResovler(接口),常用的实现类为CommonsMultipartResovler。
- 客户端传递的请求参数经过 文件解析器 的解析,解析出普通表单数据,上传的文件对象,
- 普通表单数据 -》正常接收, 文件对象 -》MultipartFile对象。
- MultipartFile中的方法transferTo()完成上传
ajax上传:
FormData data = new FormData(form表单dom对象)。获取表单数据
processData:false , ajax不序列化表单中参数。
contentType:false , ajax不指定类型。
enctype:multipart/form-data。
谨记:没有MultipartResovler是无法获取到Content-Type="multipart/form-data"类型的数据,无论是表单域数据还是文件域数据。
commons-fileupload
commons-fileupload
1.4
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
- 如果客户端就一个文件域使用一个MultipartFile对象接收就可以了。
- 如果客户端是多个同名的文件域使用MultipartFile 数组接收。
- 如果客户端是多个不同名的文件域使用多个MultipartFile对象接收就可以了。
@Controller
public class PeopleController {
/**
* 文件上传控制单元方法实现
*
* @param name 也可以使用JavaBean接收name的值
* @param address 也可以使用JavaBean接收address的值
* @param photo 名字必须和表单中文件域的name属性值相同
* @return
* @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
*/
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo,HttpServletRequest request) throws IOException {
if(!photo.isEmpty()) {
long timeMillis = System.currentTimeMillis();
Random random = new Random();
String fileName = timeMillis + "" + random.nextInt(1000);
String oldName = photo.getOriginalFilename();
String suffix = oldName.substring(oldName.lastIndexOf("."));
// 获取到当前项目images目录,发布到Tomcat后的绝对路径。
String realPath = request.getServletContext().getRealPath("/images");
System.out.println(realPath);
// 保存到当前项目的images目录中。
photo.transferTo(new File(realPath,fileName + suffix));
}
return "/upload.jsp";
}
- 在CommonsMultipartResolver中提供了setmaxUploadSize(long)方法,表示设置上传文件的大小。单位是字节byte。默认值为-1,表示无限制。
- 因为是setter方法,所以可以在配置Bean时直接进行设置注入。
如果项目需求中包含必须上传指定类型文件。在Commons-Fileupload中没有提供对应的方法,在Spring MVC中也没有提供对应的封装。所以只能自己编写逻辑进行实现。
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo,HttpServletRequest request) throws IOException {
String oldName = photo.getOriginalFilename();
String suffix = oldName.substring(oldName.lastIndexOf("."));
// 判断文件扩展名是否正确
if(suffix.equals(".png")||suffix.equals(".jpg")) {
if (!photo.isEmpty()) {
long timeMillis = System.currentTimeMillis();
Random random = new Random();
String fileName = timeMillis + "" + random.nextInt(1000);
String realPath = request.getServletContext().getRealPath("/images");
System.out.println(realPath);
photo.transferTo(new File(realPath, fileName + suffix));
}
}else{
// 可以跳转到错误页面,也可以跑出异常
}
return "/upload.jsp";
}
文件下载就是把服务器中的资源下载到本地。 当超链接访问的是浏览器本身能打开的资源。浏览器直接打开。这个特点就是响应头参数Content-Disposition控制的,其默认值为inline,表示能打开就打开,不能打开就下载。
Content-Disposition可取值有两个 :
(1)inline。直接在浏览器中打开(能打开就打开,不能打开就下载)。
(2)attachment。以附件形式下载。
@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
try {
// filename=的值就是客户端看到的下载文件名称
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
File file = new File(req.getServletContext().getRealPath("/images"), filename);
FileInputStream fis = new FileInputStream(file);
ServletOutputStream os = response.getOutputStream();
IOUtils.copy(fis, os);
} catch (IOException e) {
e.printStackTrace();
}
}
如果文件下载时包含中文名称,需要保证filename=后面的内容是ISO-8859-1编码。如果filename=后面是UTF-8编码且包含中文会乱码。
String fileNameUtf8 = new String(filename.getBytes("iso-8859-1"), "utf-8");
// 图片名称满足固定格式
String newFilenameUtf8 = "来自尚学堂的"+fileNameUtf8;
String newFilenameISO = new String(newFilenameUtf8.getBytes("utf-8"),"iso-8859-1");
// 此处是ISO-8859-1编码的内容