【Spring MVC】(六)RESTFUL 的设计风格、Spring MVC 拦截器、文件上传和下载

简介: 【Spring MVC】(六)RESTFUL 的设计风格、Spring MVC 拦截器、文件上传和下载

文章目录


一、RESTFUL 的设计风格

1、RESTFUL 概述

2、RESTFUL 设计风格示例

二、SpringMVC 拦截器

1、SpringMVC 拦截器使用步骤

三、SpringMVC 文件上传和下载

1、SpringMVC 上传文件

2、SpringMVC 文件下载


一、RESTFUL 的设计风格


1、RESTFUL 概述


(1)RESTFUL 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次。在RESTFUL的设计中服务器的任何内容都被当成资源来处理,例如网页、文件、数据库的数据等等,而同一种资源共享同一个接口地址,然后再通过请求方式的不同来区分对资源进行的不同操作。


(2)请求方式如下:


GET请求表示获取数据


POST请求表示新增数据


PUT/PATCH请求表示修改数据


DELETE请求表示删除数据


结合请求地址来看:


GET方式发送 /users请求时,表示查询users的所有数据


DELETE方式发送/users请求时,则表示删除users数据


POST方式发送/users请求时,则表示要新增一个用户数据


PUT方式发送/users请求时,则表示要更新一个用户数据


PATCH方式发送/users请求时,则表示要更新一个用户数据中的部分字段


2、RESTFUL 设计风格示例


(1)SpringMVC 提供了对RESTFUL的支持,这种支持体现在两个方面,SpringMVC 在进行请求地址映射时,不但会匹配请求地址的名称,而且还会匹配发送请求的方式,也就是说只有当请求地址和请求方式都匹配时,才能执行业务控制器中的方法。例如:

@RequestMapping(value="user",method= {RequestMethod.GET})
public ResultModel selectAll(){
  return null;
}


另外@RequestMapping()还可以用对应的请求方式代替,如:

@GetMapping("user")
public ResultModel selectAll(){
  System.out.println("查询所有");
  System.out.println(us.selectAll());
  return new ResultModel(us.selectAll(),null,"success",null);
}


GET请求对应@GetMapping


POST请求对应@PostMapping


PUT/PATCH对应@PutMapping


DELETE对应@DeleteMapping


(2)在上述的请求地址映射中,客户端通过get方式发送user请求能够执行业务控制器方法,但是如果使用了除get以外的其他请求方式,就不能够执行该方法,这个设置为同一个地址不同请求方式提供了支持。SpingMVC在请求地址中可以提供占位符,而且占位符中的数据可以在方法中获取到,这就提供了一种非常简洁的传参方式。

例如:

@GetMapping("user/{userId}")
public ResultModel selectById(@PathVariable("userId")int userId){
  return null;
}


通过上述配置,我们就可以在页面发送get方式的请求user/1表示要查询id为1的用户数据。

以下是RESTFUL所设计的一组接口(增删改查):

@RestController
public class UsersController {
  @Resource
  private UsersService us;  //业务层对象,便于调用业务层对应方法
  //定义请求地址映射第一种:@RequestMapping(value="user",method= {RequestMethod.GET})
  //定义请求地址映射第二种:@GetMapping("user"),下面都使用第二种
  @GetMapping("user")
  public ResultModel selectAll() throws Exception {
  return new ResultModel(us.selectAll(),null,"success",null);
  }
  @GetMapping("user/{userId}")
  public ResultModel selectById(@PathVariable("userId")int userId) throws Exception{
  return new ResultModel(null,us.selectById(userId),"success",null);
  }
  @PostMapping("user")
  public ResultModel insert(@Validated Users users,BindingResult br) throws Exception {
  //自定义参数校验异常判断,参数不对的时候会执行自定义异常的报错
  if(br.hasErrors()) {
    throw new ParamException();
  }
  us.insert(users);
  return new ResultModel(null,null,"success",null);
  }
  @PutMapping("user")
  public ResultModel updateById(@Validated Users users,BindingResult br) throws Exception {
  if(br.hasErrors()) {
    throw new ParamException();
  }
  us.updateById(users);
  return new ResultModel(null,null,"success",null);
  }
  @DeleteMapping("user/{userId}")
  public ResultModel deleteById(@PathVariable("userId")int userId) throws Exception {
  us.deleteById(userId);
  return new ResultModel(null,null,"success",null);
  }
}


(3)如果是PUT请求,需要添加一个额外的过滤器,控制器中才能获取到传递的参数。

<filter>
    <filter-name>HttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


接口的请求地址设计好之后,我们还需要统一对返回的JSON数据进行规范,通常可以定义一个返回值类,在返回值类中封装执行状态以及一些需要返回给前端的数据。


(4)在AJAX的项目中也可以使用异常处理器进行统一的异常处理,但是要注意的是,在异常处理器中响应视图信息时必须响应JSON类型的视图信息。

代码如下:需要导入 fastjson 的 jar 包

@Component
public class ExceptionResolver implements HandlerExceptionResolver{
  @Override
  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception ex) {
  FastJsonJsonView view = new FastJsonJsonView();
  ex.printStackTrace();
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("code", "error");
  if(ex instanceof ParamException) {
    map.put("message", "参数错误");
  }else {
    map.put("message", "服务器异常");
  }
  view.setAttributesMap(map);
  ModelAndView mav = new ModelAndView();
  mav.setView(view);
  return mav;
  }
}


(5)前端请求类

<body>
   <a href="javascript:" οnclick="selectAll()">查询所有</a>
  <table id="table">
     <tr>
    <td>姓名</td>
    <td>密码</td>
    <td>操作</td>
  </tr>
  </table>
   <form id="insert-form">
       <input name="userName"><br>
       <input type="password" name="userPassword"><br>
       <input type="button" οnclick="insert()" value="新增">
   </form>
   <input id="userId" placeholder="请输入用户id"><input type="button" value="搜索用户" οnclick="selectById()">
   <form id="update-form">
       <input  name="userId">
       <input name="userName"><br>
       <input type="password" name="userPassword"><br>
       <input type="button" οnclick="updateById()" value="修改">
   </form>
   <script type="text/javascript">
    function selectAll(){
     $.ajax({
      url: "/SpringMVC_Demo1/user",
      type: "get",
      dataType: "json",
      success:function(data){
       for(var i=0;i<data.list.length;i++){
        $("#table").append("<tr id='deleted"+data.list[i].userId+"'>"+
                      "<td>"+data.list[i].userId+"</td>"+
                      "<td>"+data.list[i].userName+"</td>"+
                      "<td>"+data.list[i].userPassword+"</td>"+
                      "<td><a href='javascript:' οnclick='deleteById("+data.list[i].userId+")'>删除</a></td>"+
                   "</tr>");
       }
      }
     });
    }
    function deleteById(userId){
     $.ajax({
               url: "/SpringMVC_Demo1/user/"+userId,
               type: "delete",
               dataType: "json",
               success:function(data){
                console.log(data.code);
                $("tr[id=deleted"+userId+"]").remove();
               }
           });
    }
    function insert(){
     $.ajax({
               url: "/SpringMVC_Demo1/user",
               type: "post",
               data: $("#insert-form").serializeJSON(),
               dataType: "json",
               success:function(data){
                   console.log(data.code);
               }
           });
    }
    function updateById(){
     $.ajax({
                url:"/SpringMVC_Demo1/user",
                type:"put",
                data:$("#update-form").serializeJSON(),
                dataType:"json",
                success:function(data){
                  console.log(data.code);
                }
            });
       }
    function selectById(){
            $.ajax({
                url:"/SpringMVC_Demo1/user/"+$("#userId").val(),
                type:"get",
                dataType:"json",
                success:function(data){
                    $("#update-form input[name=userId]").val(data.object.userId);
                    $("#update-form input[name=userName]").val(data.object.userName);
                    $("#update-form input[name=userPassword]").val(data.object.userPassword);
                }
            });
        }
   </script>
</body>


二、SpringMVC 拦截器


Spring Web MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。


1、SpringMVC 拦截器使用步骤


(1)拦截器定义,实现 HandlerInterceptor 接口

@Component
public class Interceptors implements HandlerInterceptor{
  /**
  * 请求处理之前调用拦截器
  */
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  System.out.println("请求处理之前");
  //返回true表示放行,返回false表示拦截
  return true;
  }
  /**
  * 请求处理之后
  */
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  System.out.println("请求处理后");
  }
  /**
  * 请求处理之后,提交到界面之前
  */
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  System.out.println("请求处理后,提交数据到界面之前");
  }
}


preHandle方法在执行处理器之前执行,可以用于登录验证、权限验证等等


postHandler方法在出行完处理器之后,进入视图解析器之前执行,可以对视图进行处理


afterCompletion方法在响应视图之前执行,主要用于关闭资源


(2)如果没有在拦截器类上面添加@Component注解,则需要手动配置拦截器,在 SpringMVC 的配置文件中添加标签加载拦截器

<!-- 配置拦截器 -->
<bean id="Interceptors" class="com.springmvc.interceptor.Interceptors"/>
<mvc:interceptors>
  <mvc:interceptor>
  <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/js/**"/> 
  <!--配置从根目录下发送的以js开头的所有请求不进入拦截器 -->
  <ref bean="Interceptors"/>
  </mvc:interceptor>
</mvc:interceptors>


Springmvc 不会拦截.jsp的请求,但是会拦截静态资源请求,如果不想让用户直接访问 jsp 页面,需要将 jsp 页面放到 web-inf 中,对于静态资源需要提供静态资源处理器以及通过<mvc:exclude-mapping path="/js/**"/>对静态资源放行。


三、SpringMVC 文件上传和下载


无论是 Servlet 文件上传,Struts2 文件上传还是 SpringMVC 文件上传,前端是不变的既可以使用普通的表单上传也可以使用AJAX上传。区别主要在于后端代码的处理。


1、SpringMVC 上传文件


(1)加载多部件解析器

<!-- 配置多部件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <!-- 设置上传文件的最大字节数 -->
  <property name="maxUploadSize" value="20971520"/>
  <property name="defaultEncoding" value="UTF-8"/>
</bean>


(2)页面采用表单提交或者AJAX提交

<form action="upload.do" method="post" enctype="multipart/form-data">
    <input type="file" name="head"><input type="submit" value="点击上传">
</form>


(3)SpringMVC 上传可以使用 commons-upload 的包来简化操作


20190702143624189.png


(4)Controller 中处理文件

@Controller
public class FileController {
  @RequestMapping("upload")
  @ResponseBody
  public String upload(MultipartFile head) throws Exception {
  //获取上传文件的名称
  String filename=head.getOriginalFilename();
  //获取文件的后缀
  String suffix=filename.substring(filename.lastIndexOf("."));
  //随机生成文件保存时的名字
  String name=UUID.randomUUID().toString().replace("-", "");
  filename=name+suffix;
  //获取保存文件的文件夹
  String path = request.getServletContext().getRealPath("/upload");
  File dir = new File(path);
  //判断目标文件夹是否存在,不存在则创建
  if(!dir.exists()){
    dir.mkdirs(); 
  }
  //生成目标文件夹
  File file=new File(dir,filename);
  //保存文件
  head.transferTo(file);
  //在后端直接发送一个前端页面的弹窗
  return "<script>alert('ok nb')</script>";
  }
}


2、SpringMVC 文件下载


(1)文件下载核心代码

// 设置响应头,响应类型为附件,浏览器将会用下载的方式处理响应的信息
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filePath, "UTF-8"));


(2)示例

① Controller 层
@Controller
public class FileController {
  @RequestMapping("download")
  public void download(String filename,HttpServletResponse response) throws Exception{
  //获取下载文件所在地址和文件名,拼接成下载路径
  FileInputStream fis=new FileInputStream("D:\\upload\\"+filename);
  //下载文件核心代码
  response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(filename, "UTF-8"));
  //创建输出流写入文件
  OutputStream os=response.getOutputStream();
  byte[] b=new byte[1024];
  int length=0;
  while((length=fis.read(b))>0) {
    os.write(b, 0, length);
  }
  fis.close();
  os.close();
  }
}


② 前端发送请求简单示例

<a href="download.do?filename=1.zip">点击下载</a>



目录
相关文章
|
6天前
|
前端开发 Java Spring
Spring MVC 是如何对对象参数进行校验的
【6月更文挑战第4天】对象参数校验是使用 SpringMVC 时常用的功能,这篇文章尝试分析了,Spring 是如何实现这一功能的。
20 5
|
30天前
|
前端开发 Java 测试技术
Java一分钟之Spring MVC:构建Web应用
【5月更文挑战第15天】Spring MVC是Spring框架的Web应用模块,基于MVC模式实现业务、数据和UI解耦。常见问题包括:配置DispatcherServlet、Controller映射错误、视图解析未设置、Model数据传递遗漏、异常处理未配置、依赖注入缺失和忽视单元测试。解决这些问题可提升代码质量和应用性能。注意配置`web.xml`、`@RequestMapping`、`ViewResolver`、`Model`、`@ExceptionHandler`、`@Autowired`,并编写测试用例。
309 3
|
1天前
|
前端开发 Java Spring
Spring MVC 请求处理流程
Spring MVC 请求处理流程
4 0
|
2天前
|
Java API 网络架构
【Spring Boot】详解restful api
【Spring Boot】详解restful api
10 0
|
5天前
|
JSON 前端开发 Java
Spring MVC 级联对象参数校验
【6月更文挑战第6天】在 Spring MVC 的使用过程中,我们会发现很多非常符合直觉的功能特性,但往往我们会习惯这种「被照顾得很好」的开发方式,依靠直觉去判断很多功能特性的用法。
9 1
|
7天前
|
前端开发 Java Spring
自定义 Spring MVC Controller 方法参数处理
【6月更文挑战第3天】在 Spring MVC Controller 的方法参数,Spring 会自动为我们注入一些特殊的参数值,比如 HttpServletRequest、HttpServletResponse 等对象,或者 HTTP 请求参数。
49 0
|
8天前
|
Web App开发 前端开发 Java
基于Spring3 MVC实现基于HTML form表单文件上传
基于Spring3 MVC实现基于HTML form表单文件上传
17 7
基于Spring3 MVC实现基于HTML form表单文件上传
|
9天前
|
JSON 前端开发 Java
Spring第四课,MVC终章,应用分层的好处,总结
Spring第四课,MVC终章,应用分层的好处,总结
|
10天前
|
JSON 前端开发 Java
Spring Web MVC入门(3)——响应
Spring Web MVC入门(3)——响应
13 1
|
10天前
|
存储 前端开发 Java
Spring Web MVC入门(2)——请求(下)
Spring Web MVC入门(2)——请求
17 0