Spring MVC:轻量级MVC框架2.0

一、Restful请求格式

1. 介绍

Rest(Representational State Transfer:表现层状态转移)是一种软件架构风格,其核心是面向资源的一种设计。

2. 正常使用

Restful要求在当前的url地址中直接嵌套请求数据。

restful中要求使用不同的请求方式来标记对资源的操作方式,达到调用不同的后台功能方法来处理请求的目的。  

/**
 * @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显示页面

使用Restful只写一个控制器。  

@RequestMapping("/{yemian}")
public String showPage(@PathVariable String yemian){
    return "/WEB-INF/page/"+yemian+".jsp";
}

二、@ResponseBody注解

1. @ResponseBody介绍

 @ResponseBody注解是类或方法级注解。

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

        直接在方法上添加上@ResponseBody,Spring MVC会把返回值设置到响应流中。 等效于直接使用PrintWriter对象进行打印。

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

2. 设置响应内容类型

 在使用@ResponseBody注解时,只要返回值类型不是类或Map或List等满足键值对类型。Spring MVC 都会设置响应内容类型为text/html;charset=ISO-8859-1。

想要改变@ResonseBody注解的响应内容类型(Content-Type)只能通过@RequestMapping的produces属性进行设置。  

@RequestMapping(value="/demo1",produces = "text/html;charset=utf-8")
@ResponseBody
public String demo1() {
    return "张三";
}

3. 自动转换为JSON字符串

@ResponseBody注解可以把控制单元返回值自动转换为JSON字符串。主要完成下面几个事情:  

(1)判断返回值是否为JavaBean、JavaBean数组、List<JavaBean类型>、Map等满足键值对的类型。

(2)如果满足键值对类型,会使用Jackson把对象转换为JSON字符串,设置到响应流中。同时会设置响应内容类型(Content-Type)为application/json;charset=utf-8

因为Spring MVC默认使用Jackson作为JSON转换工具,所以必须保证项目中存在Jackson的依赖。

4. 转换为XML文件

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

5. @RestController注解

 对于页面中使用前端框架时的项目。例如页面时通过:EasyUI、BootStrap、Vue等前端框架进行编写时,客户端向服务端发送的请求都是异步Ajax(或类似Ajax的异步请求)。对于这样的项目,控制器中所有的方法都包含@ResponseBody注解。

@RestController  // 此处换成了@RestController,而不是@Controller了
public class DemoController {

    // 下面所有方法都不写@ResponseBody注解
    
    @RequestMapping("/demo")
    public Map<String,Object> demo(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","张三");
        map.put("age",18);
        return map;
    }

三、@RequestBody注解

1. 介绍

@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List<Map>等类型。  

如果希望在单体架构项目中使用@RequestBody注解,需要在客户端中使用Ajax请求,刻意设置请求的内容类型(Content-Type)为JSON或XML。  

2. 修改请求内容类型

 如果希望修改请求内容类型,可以使用HTML的<form>中enctype属性或使用Ajax中contentType属性进行设置。

 <form>的enctype属性一般只有在文件上传时才会修改,所以希望传递特定类型请求参数内容时,都是通过Ajax进行请求

$.ajax({
    url:"testContentType",
    contentType:"application/json",// 修改请求内容类型为JSON
    data:'{"id":1,"name":"张三"}',// 取值两次必须有单引号,没有单引号无效
    type:"post",// 不能是GET类型请求
    success:function (data) {
        console.log(data);
    },
    dataType:"json"
});

四、Spring MVC文件上传

1. 文件上传介绍

 文件上传就是把客户端的文件上传到服务端进行保存。在文件上传时文件和其他请求参数是在请求体中进行传递。所以不支持GET类型请求。

表单内容类型application/x-www-form-urlencoded不支持传递文件流。所以需要在<form>的enctype中设置enctype="multipart/form-data"才表示把文件和其他表单参数设置到请求体中。  

 

总结出来,Spring MVC 文件上传有如下几点要求:

(1)客户端:

(1.1) 请求方式必须是POST

(1.2)enctype必须为multipart/form-data

(2)服务端:

(2.1)必须配置MultipartResovler。否则无法解析上传文件的流数据。(<bean>的id值必须叫做multipartResovler)如果没有配置MultipartResovler不仅仅是文件流数据无法解析,连带着其他表单域数据也无法解析。因为文件流数据和表单数据都在请求体中,不解析的话,文件流数据和表单数据都接收不到。

(2.2)注意文件域的name取值,文件域必须MultipartFile类型接收。且name的取值必须和MultipartFile对象名相同。

配置上传解析器bean

只有配置了MultipartResovler,Spring MVC 才会解析上传文件流数据。

同时<bean>的id必须叫做multipartResovler,叫其他名字无效。

<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
@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) throws IOException {
        photo.transferTo(new File("D:/images", photo.getOriginalFilename()));
        return "/upload.jsp";
    }
}

2. 生成唯一文件名

在上面代码中,保存文件名称时是使用文件上传时的名称进行保存。这样做存在一个问题:如果存在同名文件,后上传文件会覆盖之前文件内容。

所以在文件上传时都会生成一个全局唯一的文件名。常见有两种方式:

(1)时间戳+随机数

(2)UUID

/**
     * 文件上传控制单元方法实现
     * @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) throws IOException {
        // 判断上传文件流是否为空。如果不为空继续执行
        if(!photo.isEmpty()) {
            // 使用UUID生成文件名称
            // String fileName = UUID.randomUUID().toString();
            // 使用时间戳+随机数生成文件名
            long timeMillis = System.currentTimeMillis();
            Random random = new Random();
            String fileName = timeMillis + "" + random.nextInt(1000);
            // 获取上传时文件名
            String oldName = photo.getOriginalFilename();
            // 获取上传时文件的扩展名
            String suffix = oldName.substring(oldName.lastIndexOf("."));
            // 保存文件到D:/images中。必须保存D盘下已经存在images文件夹
            photo.transferTo(new File("D:/images",fileName + suffix));
        }
        return "/upload.jsp";
    }

3. 保存文件到当前项目中

        保存到项目发布到Tomcat的目录而不是源码目录。在使用Tomcat插件时,target/项目名-版本 目录为项目编译后发布到Tomcat的目录。

 

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

4. 限制上传文件大小

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

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="1024"></property>
</bean>

五、Spring MVC文件下载

1. 文件下载介绍

 文件下载就是把服务器中的资源下载到本地。

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

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

 实现访问控制器,并传递需要下载文件名称。

<a href="/download?filename=a.png">a.png</a>

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

 如果文件下载时包含中文名称,需要保证filename=后面的内容是ISO-8859-1编码。如果filename=后面是UTF-8编码且包含中文会乱码。

改写控制器代码,需要反复进行编码转换  

@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
    try {
        // 因为是GET请求,所以要解决请求参数中文乱码问题
        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编码的内容
        response.setHeader("Content-Disposition", "attachment;filename=" + newFilenameISO);
        // 此处必须是UTF-8解决参数乱码问题的名称
        File file = new File(req.getServletContext().getRealPath("/images"), fileNameUtf8);
        FileInputStream fis = new FileInputStream(file);
        ServletOutputStream os = response.getOutputStream();
        IOUtils.copy(fis, os);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值