09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理

1.处理JSON
  • @RequestBody和@ResposeBody
    • @RequestBody
      • 用来将请求报文转换为方法入参中的某个类型的对象
      • 注解只能添加到方法的入参前面
      • 例如:将上传文件的流转换为方法入参中的一个字符串类型
        • 表单
<formaction="${pageContext.request.contextPath }/testRequestBody"method="post"enctype="multipart/form-data">
         描述:<inputtype="text"name="desp"><br>
         文件:<inputtype="file"name="wj"><br>
         <inputtype="submit">
 </form>
  • 测试方法
@RequestMapping("/testRequestBody")
    publicString testRequestBody(@RequestBodyStringbody){
         System.out.println("请求报文中的信息是:"+body);
         return"success";
    }
这里如果是要显示保文信息就不用写
 <beanid="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       设置字符集
       <propertyname="defaultEncoding"value="UTF-8"></property>
       设置文件的大小
       <propertyname="maxUploadSize"value="102400"></property>
    </bean>
  • 也可以通过在方法的入参中传入HttpEntity<T>对象来实现类似的功能
  • @ResposeBody
    • 用来将Handler的方法的返回值以某种格式响应给前端
    • 该注解可以添加到类上,也可以添加到方法上
      • 如果添加到类上,Handler中的所有方法的返回值将直接响应给页面
@ResponseBody//如果该注解添加到类上,类中所有的方法的返回值将直接响应给浏览器
@Controller
publicclass SpringMVCHandler {
  • 如果添加到方法上,只有添加了该注解的方法的返回值会直接响应給页面
@ResponseBody
    @RequestMapping("/testResponseBody")
    publicString testResponseBody(){
         System.out.println("测试ResponseBody");
         return"success";//当Handler的方法上添加了@ResponseBody注解之后返回值将直接响应给浏览器 
     JSP页面显示的是success
    }
    @ResponseBody
    @RequestMapping("/testResponseBody2")
    publicString testResponseBody2(){
         System.out.println("测试ResponseBody2");
         return"page";//当Handler的方法上添加了@ResponseBody注解之后返回值将直接响应给浏览器
    }
  • 也可以通过将方法的返回值设置为ResponseEntity<T>来实现类似的功能
    • 实现文件下载的功能
//实现文件下载的效果
    @RequestMapping("/testResponseEntity")
    publicResponseEntity<byte[]> testResponseEntity(HttpSession session)throws IOException{
         //获取ServletContext对象
         ServletContextservletContext = session.getServletContext();
         //获取输入流
         InputStreamis = servletContext.getResourceAsStream("/download/meinv.jpg");
         //创建byte数组
         byte[]body = new byte[is.available()];
         //将流读到数组中
         is.read(body);
         //设置响应头
         HttpHeadersheaders = new HttpHeaders();
         //告诉浏览器如果处理文件
         headers.add("Content-Disposition","attachment; filename=mn.jpg");
         //设置Http的状态码为OK
         HttpStatusstatusCode = HttpStatus.OK;
         //创建ResponseEntity
         ResponseEntity<byte[]>responseBody = new ResponseEntity<byte[]>(body,headers,statusCode);
         returnresponseBody;
    }
  • 实现文件上传功能
//文件上传
@RequestMapping("/testFileUpload")
    public String testFileUpload(@RequestParam("desp") String desp ,
            @RequestParam("wj") MultipartFile file , HttpSession session) throws IllegalStateException, IOException{
        System.out.println("文件的描述性信息是:"+desp);
        //获取文件名
        String fileName = file.getOriginalFilename();
        //获取文件的类型
        String contentType = file.getContentType();
        //获取文件的大小
        long size = file.getSize();
        System.out.println("文件名是:"+fileName);
        System.out.println("文件的类型是:"+contentType);
        System.out.println("文件的大小是:"+size+"字节");
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取upload目录在服务器端的真实路径
        String realPath = servletContext.getRealPath("/upload");
        //判断服务端是否有upload目录,如果没人,让它自动创建
        File upload = new File(realPath);
        if(!upload.exists()){
            //创建该目录
            upload.mkdirs();
        }
        //将文件上传到upload目录中
        file.transferTo(new File(realPath+"/"+fileName));
        return "success";
    }

在springmvc.xml中需要配置以下
<beanid="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       <!-- //设置字符集 -->
       <propertyname="defaultEncoding"value="UTF-8"></property>
       <!-- 设置文件的大小 -->
       <propertyname="maxUploadSize"value="102400"></property>
    </bean>

  • 处理JSON
    • 导入以下jar包
    jackson-annotations-2.1.5.jar
    jackson-core-2.1.5.jar
    jackson-databind-2.1.5.jar

  • 发送一个Ajax请求并在处理请求的方法上添加@RequestBody注解
    • 发送Ajax请求
<buttonid="btn">Test JSON</button><br>

$(function(){
         //给按钮绑定单击事件
         $("#btn").click(function(){
             //发送Ajax请求
             //设置请求地址
             varurl ="${pageContext.request.contextPath }/testJSON"
             $.post(url,function(data){
                 for(vari = 0; i < data.length; i++){
                     alert(data[i].id+"-"+data[i].username);
                 }
             });
         });
    });


  • 处理请求
@ResponseBody
    @RequestMapping("/testJSON")
    publicList<User> testJSON(){
         System.out.println("通过发送Ajax请求处理JSON数据");
         //创建一个List<User>
         List<User>list = new ArrayList<>();
         //向list中添加User对象
         list.add(newUser(1,"虚刚","123456","[email protected]"));
         list.add(newUser(2,"许刚","222222","[email protected]"));
         list.add(newUser(3,"虚虚刚","444444","[email protected]"));
         returnlist;
    }
  • 能实现上述功能是通过HttpMessageConverter<T>接口的具体实现类来实现的
09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理
2.国际化
  • SpringMVC通过LocaleResolver解析器获取本地化信息,默认使用的解析器是AcceptHeaderLocaleResolver,这时发请求时会自动获取浏览器中的Accept-Language的属性值来实现国际化
  • 实现国际化需要在SpringMVC的配置文件中配置国际化资源文件
<!-- 设置国际化资源文件 -->
    <beanid="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource">
         <!-- 设置国际化资源文件的基础名 -->
         <propertyname="basename"value="i18n"></property>
    </bean>
  • 我们可以通过超链接的形式来实现中英文的切换
    • 1)在SPringMVC的配置文件中配置SessionLocaleResolver解析器和LocaleChangeInterceptor拦截器
<!-- 配置SessionLocaleResolver -->
    <beanid="localeResolver"class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    
    <!-- 配置LocaleChangeInterceptor -->
    <!-- 拦截器额执行顺序由配置的先后顺序决定 -->
    <mvc:interceptors>
         <beanclass="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
             <!-- 通过paramName属性设置超链接中传入的语言及国家信息的请求参数 -->
             <propertyname="paramName"value="language"></property>
         </bean>
    </mvc:interceptors>
  • 2)前端的超链接发送请求时需要携带一个请求参数,参数的默认的名字是locale,我们可以在LocaleChangeIntercepto中设置这个参数名
<ahref="${pageContext.request.contextPath }/testCutomerI18N?language=zh_CN">中文</a>|
    <ahref="${pageContext.request.contextPath }/testCutomerI18N?language=en_US">ENGLISH</a>
<!-- 设置不经过Handler的方法直接响应的页面 -->
    <mvc:view-controller path="/testCutomerI18N" view-name="success"/>
  • 通过1)和2)的设置之后,当我们点击超链接时,会将语言及国家的信息设置到session域中,以后在发送请求会直接从session域中获取语言及国家的信息,以此来实现国际化的操作
3.文件的上传
  • 1)需要导入以下jar包
    commons-fileupload-1.3.1.jar
    commons-io-2.5.jar
  • 2)在SpringMVC的配置文件中配置CommonsMultipartResovler解析器
<!-- 配置CommonsMultipartResovler -->
    <beanid="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
         <!-- 设置字符集 -->
         <propertyname="defaultEncoding"value="UTF-8"></property>
         <!-- 设置文件的大小 -->
         <propertyname="maxUploadSize"value="102400"></property>
    </bean>
  • 3)上传文件的表单
<formaction="${pageContext.request.contextPath }/testFileUpload"method="post"enctype="multipart/form-data">
         描述:<inputtype="text"name="desp"><br>
         文件:<inputtype="file"name="wj"><br>
         <inputtype="submit">
    </form>
  • 4)处理文件上传的方法,该方法中需要传入一个MultipartFile类型的一个参数
@RequestMapping("/testFileUpload")
    publicString testFileUpload(@RequestParam("desp") String desp ,
             @RequestParam("wj")MultipartFile file , HttpSession session)throws IllegalStateException, IOException{
         System.out.println("文件的描述性信息是:"+desp);
         //获取文件名
         StringfileName = file.getOriginalFilename();
         //获取文件的类型
         StringcontentType = file.getContentType();
         //获取文件的大小
         longsize = file.getSize();
         System.out.println("文件名是:"+fileName);
         System.out.println("文件的类型是:"+contentType);
         System.out.println("文件的大小是:"+size+"字节");
         //获取ServletContext对象
         ServletContextservletContext = session.getServletContext();
         //获取upload目录在服务器端的真实路径
         StringrealPath = servletContext.getRealPath("/upload");
         //判断服务端是否有upload目录,如果没人,让它自动创建
         Fileupload = new File(realPath);
         if(!upload.exists()){
             //创建该目录
             upload.mkdirs();
         }
         //将文件上传到upload目录中
         file.transferTo(newFile(realPath+"/"+fileName));
         return"success";
    }
4.拦截器
  • 自定义拦截器需要实现HandlerInterceptor接口
publicclass FirstInterceptor implements HandlerInterceptor {
    /**
     * 在调用目标方法之前执行
     *
     * 可以用来设置权限、日志、事务
     */
    @Override
    publicboolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Objecthandler)
             throwsException {
         System.out.println("FirstInterceptor的preHandle方法被调用");
         returntrue;
    }
    /**
     * 调用目标之后渲染视图之前执行
     *
     * 可以修改ModelAndView中模型数据及视图
     */
    @Override
    publicvoid postHandle(HttpServletRequest request, HttpServletResponseresponse, Objecthandler,
             ModelAndViewmodelAndView)throws Exception {
         System.out.println("FirstInterceptor的postHandle方法被调用");
    }
    /**
     * 渲染视图之后执行
     *
     * 可以用来释放资源
     */
    @Override
    publicvoid afterCompletion(HttpServletRequest request, HttpServletResponseresponse, Objecthandler, Exceptionex)
             throwsException {
         System.out.println("FirstInterceptor的afterCompletion方法被调用");
    }
}
  • 在SpringMVC中配置拦截器
<!-- 配置LocaleChangeInterceptor -->
    <!-- 拦截器额执行顺序由配置的先后顺序决定 -->
    <mvc:interceptors>
         <beanclass="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
             <!-- 通过paramName属性设置超链接中传入的语言及国家信息的请求参数 -->
             <propertyname="paramName"value="language"></property>
         </bean>
        <!-- 通过以下这种方式配置的拦截器会拦截所有的请求 -->
         <beanclass="com.atguigu.springmvc.interceptor.FirstInterceptor"></bean>
         <!-- 也可以通过以下方式配置拦截器,通过这种方式可以配置拦截和不拦截那些请求 -->
         <mvc:interceptor>
             <!-- 设置拦截的请求 -->
             <mvc:mappingpath="/testInterceptor"/>
             <!-- 设置不拦截那些请求 -->
<!--             <mvc:exclude-mapping path=""/> -->
             <beanclass="com.atguigu.springmvc.interceptor.SecondInterceptor"></bean>
         </mvc:interceptor>
    </mvc:interceptors>
  • 具体多个拦截器的执行顺序请参照源码
  • 09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理
  • 09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理

5.异常处理
  • Spring MVC 通过 HandlerExceptionResolver  处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常
  • Spring MVC 提供的 HandlerExceptionResolver的实现类(打红色方框的)
09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理09.SpringMVC_处理JSON&国际化&文件上传&拦截器&异常处理

  • @ExceptionHandler注解
    • 通过该注解标识一个处理异常的方法
@ExceptionHandler(value=ArithmeticException.class)
    //定义一个处理异常的方法
    publicString resoveException(Exceptione){
         System.out.println("异常信息是:"+e);
         return"error";
    }
    //异常匹配的优先级:根据继承关系优先匹配
    @ExceptionHandler(value=RuntimeException.class)
    //定义一个处理异常的方法
    publicString resoveException2(Exceptione){
         System.out.println("[异常信息是]:"+e);
         return"error";
    }
    @ExceptionHandler(value=Exception.class)
    //定义一个处理异常的方法
    publicString resoveException3(Exceptione){
         System.out.println("【异常信息是】:"+e);
         return"error";
    }
  • 在方法上添加了@ExceptionHandler注解之后,该方法只能处理当前Handler的方法执行时出现的异常,如果想定义一个全局的处理异常的方法,我们可以使用@ControllerAdvice注解标识一个处理异常的类
@ControllerAdvice
//如果当前Handler中没有处理异常的方法,会去标识了@ControllerAdvice注解的类中去寻找处理异常的方法
publicclass ResolveExceptionClass {
    //定义一个处理异常的方法
    @ExceptionHandler(value=Exception.class)
    publicString resoveException3(Exceptione){
         System.out.println("{异常信息是}:"+e);
         return"error";
    }
}

实例:
JSP:
<h2>出错信息</h2>${requestScope.exception }
类中
packageexception;
importorg.springframework.web.bind.annotation.ControllerAdvice;
importorg.springframework.web.bind.annotation.ExceptionHandler;
importorg.springframework.web.servlet.ModelAndView;
@ControllerAdvice
publicclass ResolveExceptionClass {
    //定义一个处理异常的方法
   @ExceptionHandler(value=Exception.class)
   public ModelAndView resoveException3(Exception e){
         System.out.println("{异常信息是}:"+e);
         ModelAndViewmv = new ModelAndView("error");
        mv.addObject("exception",e);
        mv.setViewName("error");
        return mv;
    }
}
异常对象不能通过Map集合方式传递给成功页面,可以通过ModelAndView将异常对象传递给成功页面上
  • @ResponseStatus
    • 通过该注解可以标识一个异常类,当出现该异常时会去指定的异常页面
@ResponseStatus(value=HttpStatus.UNAUTHORIZED,reason="滚犊子!你没有此权限!!!")
publicclass UnAuthorizedException extends RuntimeException {
    /**
     *
     */
    privatestatic final long serialVersionUID = 1L;
}
  • 测试方法
@RequestMapping("/testResponseStatus")
    publicString testResponseStatus(@RequestParam("username") String username){
         if(!"superAdmin".equals(username)){
             //抛出没有权限的异常
             thrownew UnAuthorizedException();
         }
         return"success";
    }