五:Day10_SpringMVC02

@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;
    }
}

一、Restful请求格式

1. 介绍

Rest(Representational State Transfer:表现层状态转移)是一种软件架构风格,其核心是面向资源的一种设计。何为面向资源,意思是网络上的所有事物都可以抽象为资源,而每个资源都有唯一的资源标识,对资源的操作不应该改变这些标识。

通俗讲就是每个资源都有一个url地址,而不是不同的操作有不同的url地址,比如我们对用户信息的增删改查,用户就是资源,增删改查是操作,以前我们是一个操作一个url地址,现在按照Restful的说法,url地址只能有一个。

2. 正常使用

 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";
}

3. 使用Restful显示页面

我们知道,为了提高安全性,可以把页面放入到WEB-INF中。但是放入到WEB-INF中之后,访问页面之前必须先执行控制器,可以使用Restful方式显示页面,这样可以大大减少显示页面的控制器数量。

五:Day10_SpringMVC02_第1张图片

 /*
   其中映射路径前面的/page/不是强制要求,随意在前面添加的内容。
   也可以不写/page/,也可以多加几级路径。
*/
@RequestMapping("/page/{yemian}")
public String showPage(@PathVariable String yemian){
    return "/WEB-INF/page/"+yemian+".jsp";
}

二、@ResponseBody注解(重要)

1. @ResponseBody介绍

@ResponseBody注解是类或方法级注解。 默认情况下控制单元只能返回String类型的数据。返回其他数据类型出现406状态码。@ResponseBody注解可以对返回值进行设置。

2. 最简单使用

当方法上添加@ResponseBody注解后,控制单元方法返回值将不再被视图解析器进行解析|不会使用转发。而是把返回值放入到响应流中进行响应。等效于直接  使用PrintWriter对象进行打印。

@RequestMapping("/demo1")
@ResponseBody
public String demo1(){
    return "bjsxt";
}return "bjsxt";}

访问控制器后的效果是在浏览器直接打印bjsxt字符串。

3. 设置响应内容类型

  • 在使用@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 "北京尚学堂";
}

4. 返回值自动转换为JSON字符串

@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。

5. 返回值转换为XML文件(知道)

在Spring MVC中支持把返回值转换为XML文件。如果还是使用jackson-databind依赖,默认只能转换返回值为类类型的控制单元,返回值为List是无法转换为XML的,同时还要求实体类上必须有@XmlRootElement,才能转换。

如果项目中所有控制单元返回值结果都希望是XML格式,可以按照下面步骤完成。

5.1 导入依赖(xml)

    com.fasterxml.jackson.dataformat
    jackson-dataformat-xml
    2.9.9
5.2 编写控制单元

控制单元方法和转换为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;
}

6. @RestController注解(重要)

客户端向服务端发送的请求都是异步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注解

1. 介绍

  • @RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List等类型
  • 既然是转换为请求体数据,所以不能是GET类型请求(GET没有请求体),多用在POST类型的请求中。
  • 如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML。

2. 请求内容类型详解

在客户端中无论使用的是

表单,还是Ajax请求,post请求内容类型都是application/x-www-form-urlencoded,表示普通表单参数。

2.1 Ajax请求参数
  • 使用Ajax请求时,默认的参数类型也是普通表单参数(Form Data)。
  • 服务端控制器中接收参数还是和正常参数接收相同。
@RequestMapping("/testContentType")
@ResponseBody // 客户端希望要JSON数据,所以一定要有这个注解
public People testContentType(People peo) {
    System.out.println(peo);
    return peo;
}

3. 修改请求内容类型

  • 如果希望修改请求内容类型,可以使用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对象

 4.注意

  1. 因为一个请求只有一个请求体。控制单元参数中绝对不允许出现两个@RequestBody注解。

  2. 因为@RequestBody底层使用Jackson,所以只适用于把请求体数据转换为JavaBean或Map。绝对不能在@RequestBody后面使用String等类型接收请求体内容。

四、Spring MVC文件上传

1. 文件上传要求

1.1.前端要求
  • 请求方式为post请求,使用请求体完成文件的传输。
  • enctype:multipart/form-data。
1.2.后端要求
  • 引入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"类型的数据,无论是表单域数据还是文件域数据。

五:Day10_SpringMVC02_第2张图片

2. 文件上传实现流程

2.1 导入依赖

    commons-fileupload
    commons-fileupload
    1.4
2.2 在页面中编写文件上传代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title
    
    



    姓名:
年龄:
头像:
简历:
2.3 配置上传解析器bean

2.4 编写单元方法处理上传请求
  • 普通表单数据还是直接使用键名获取即可,文件数据上传的时候解析后会被存储到MultipartFile对象中。
  • MultipartFile是一个接口,接口中存在很多方法。其中:getInputStream()、getOriginalFilename()、transferTo(File)为常用方法。

五:Day10_SpringMVC02_第3张图片

  • 如果客户端就一个文件域使用一个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";
}

3. 限制上传文件大小

  • 在CommonsMultipartResolver中提供了setmaxUploadSize(long)方法,表示设置上传文件的大小。单位是字节byte。默认值为-1,表示无限制。
  • 因为是setter方法,所以可以在配置Bean时直接进行设置注入。

五:Day10_SpringMVC02_第4张图片


    

4. 限制上传文件类型

如果项目需求中包含必须上传指定类型文件。在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";
}

五、Spring MVC文件下载

1. 文件下载介绍

文件下载就是把服务器中的资源下载到本地。 当超链接访问的是浏览器本身能打开的资源。浏览器直接打开。这个特点就是响应头参数Content-Disposition控制的,其默认值为inline,表示能打开就打开,不能打开就下载

Content-Disposition可取值有两个 :

(1)inline。直接在浏览器中打开(能打开就打开,不能打开就下载)。

(2)attachment。以附件形式下载。

2. attachment

  • 如果希望所有的文件都是下载,而不是能打开则打开。可以在响应头中设置Content-Disposition参数为attachment。attachment结合filename可以设置下载。
  • 因为需要设置响应头,所以就必须编写一个下载的控制器。

@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();
    }
}

3. 文件下载中包含中文名称解决办法

如果文件下载时包含中文名称,需要保证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编码的内容

你可能感兴趣的:(spring,java)