SpringMVC-2-响应数据与视图、文件上传、异常处理、拦截器

# 内容
    1. 响应数据和结果视图
        * 返回值的分类
        * ResponseBody响应数据
    2. SpringMVC实现文件上传
    3. SpringMVC的异常处理
    4. SpringMVC框架的拦截器


## 响应数据和结果视图
    1. 返回值分类
        1. 字符串:Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。
        2. void:使用request或response响应结果
            1. 使用 request 转向页面

request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response); 

            2. 使用 response 页面重定向

response.sendRedirect("testRetrunString")

            3. 使用 response 指定响应结果,例如响应 json 数据

response.setCharacterEncoding("utf-8"); 
response.setContentType("application/json;charset=utf-8"); 
response.getWriter().write("json串"); 

        3. ModelAndView:
            * ModelAndView 是 SpringMVC 为我们提供的一个对象,可以用来调整具体的JSP视图 
            * ModeAndView 对象的两个方法如下:

//作用:添加模型到该对象中
public ModelAndView addObject(String attributeName,Object attributeValue){
     getModelMap().addAttribute(attributeName,attributeValue);
     //我们可以在页面上使用EL表达式获取attribute的值,${attributeName}
     return this;
}
//作用:用于设置逻辑视图名称,视图解析器会根据名称前往指定的视图
public void setViewName(@Nullable String viewNaem){
    this.view=viewName;
}

    2. SpringMVC框架提供的重定向和转发
        1. forward转发
            * 使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。 
            * controller方法在提供了 String 类型的返回值之后,默认就是请求转发。
            * 注意:如果用了 forward:则路径必须写成实际视图 url,不能写逻辑视图。 
                如:return "success" = return "forward:/WEB-INF/pages/success.jsp";

        2. Redirect重定向
            * 注意:如果是重定向到 jsp 页面,则 jsp 页面不能写在 WEB-INF 目录中,否则无法找到。 

    3. ResponseBody响应json数据
        1. 设置静态资源(img,css,js不被DispatcherServlet拦截,在SpringMVC配置文件中作如下配置
            * location元素表示webapp目录下的static包下的所有文件;
            * mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b;
            * 该配置的作用是:DispatcherServlet不会拦截以/static开头的所有请求路径,并当作静态资源交由Servlet处理。
            代码如下:

<!-- 设置静态资源不过滤 -->    
<mvc:resources location="/css/" mapping="/css/**"/>  <!-- 样式 -->    
<mvc:resources location="/images/" mapping="/images/**"/>  <!-- 图片 -->    
<mvc:resources location="/js/" mapping="/js/**"/>  <!-- javascript --!>

        2. 使用@RequestBody获取请求体数据

            jsp代码:
            $(function(){        
                // 绑定点击事件        
                $("#btn").click(function(){            
                    $.ajax({                
                        url:"user/testJson",                
                        contentType:"application/json;charset=UTF-8",                
                        data:'{"addressName":"aa","addressNum":100}',                
                        dataType:"json",                
                        type:"post",                
                        success:function(data){                    
                            alert(data);                    
                            alert(data.addressName);                
                            }            
                        });       
                    });    
                });
            控制器中的代码:
            @RequestMapping("/testJson")    
            public void testJson(@RequestBody String body) {        
                System.out.println(body);    
            }

        3. json字符串与JavaBean对象的转换,需要加入jackson的依赖

             <dependency>            
                <groupId>com.fasterxml.jackson.core</groupId>            
                <artifactId>jackson-databind</artifactId>            
                <version>2.9.0</version>        
            </dependency>        
            <dependency>            
                <groupId>com.fasterxml.jackson.core</groupId>            
                <artifactId>jackson-core</artifactId>            
                <version>2.9.0</version>        
            </dependency>        
            <dependency>            
                <groupId>com.fasterxml.jackson.core</groupId>            
                <artifactId>jackson-annotations</artifactId>            
                <version>2.9.0</version>        
            </dependency>

        4. 使用@RequestBody注解把json的字符串转换成JavaBean的对象
            * 在控制器的方法的形参中使用@RequestBody,将请求的json对象转换为JavaBean对象

                 $(function(){        
                    // 绑定点击事件        
                    $("#btn").click(function(){           
                        $.ajax({                
                            url:"user/testJson",               
                            contentType:"application/json;charset=UTF-8",                
                            data:'{"addressName":"aa","addressNum":100}',                
                            dataType:"json",                
                            type:"post",                
                            success:function(data){                    
                                alert(data);                    
                                alert(data.addressName);                
                                }            
                            });        
                        });    
                    });   
    
                @RequestMapping("/testJson")    
                public void testJson(@RequestBody Address address) {  
                    //JavaBean对象Address      
                    System.out.println(address);    
                }

        5. 使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应 
            * 在控制器的方法的 返回值 使用@RequestBody,将返回JavaBean对象 

                 $(function(){        
                // 绑定点击事件        
                $("#btn").click(function(){            
                    $.ajax({                
                        url:"user/testJson",                
                        contentType:"application/json;charset=UTF-8",
                        data:'{"addressName":"哈哈","addressNum":100}',
                        dataType:"json",                
                        type:"post",                
                        success:function(data){                    
                            alert(data);                    
                            alert(data.addressName);                
                            }            
                        });        
                    });    
                }); 
                
                @RequestMapping("/testJson")    
                public @ResponseBody Address testJson(@RequestBody Address address) {        
                    System.out.println(address);      
                    address.setAddressName("上海");        
                    return address;    
                }


## 文件上传
    前提要求:
        * form表单的enctype(表单请求正文的类型)取值必须是:multipart/form-data(默认值是:application/x-www-form-urlencoded)
        * method属性取值必须是Post
        * 提供一个文件选择域<input type="file"/>

    1. 传统方式实现文件上传
        1. 导入jar包
            注:commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它工作时需要 commons-io 包的支持。 

            <dependency>
                <groupId>commons-fileupload</ groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.4</version>
            </dependency>

        2. 编写文件上传的JSP页面    

                <h3>传统文件上传</h3>
                <form action="/user/fileupload1" method="post" enctype="multipart/form-data">
                    选择文件:<input type="file" name="upload" /><br/>
                    <input type="submit" value="上传" />
                </form>

        3. 编写文件上传的Controller控制器

            /**
             * 传统文件上传
             */
            @Controller
            @RequestMapping("/user")
            public class UserController {
                @RequestMapping("/fileupload1")
                public String fileuoload1(HttpServletRequest request) throws Exception {
                    System.out.println("文件上传...");
            
                    // 使用fileupload组件完成文件上传
                    // 上传的位置
                    String path = request.getSession().getServletContext().getRealPath("/uploads/");
                    // 判断,该路径是否存在
                    File file = new File(path);
                    if(!file.exists()){
                        // 创建该文件夹
                        file.mkdirs();
                    }
            
                    // 解析request对象,获取上传文件项
                    DiskFileItemFactory factory = new DiskFileItemFactory();
                    ServletFileUpload upload = new ServletFileUpload(factory);
                    // 解析request
                    List<FileItem> items = upload.parseRequest(request);
                    // 遍历
                    for(FileItem item:items){
                        // 进行判断,当前item对象是否是上传文件项
                        if(item.isFormField()){
                            // 说明普通表单向
                        }else{
                            // 说明上传文件项
                            // 获取上传文件的名称
                            String filename = item.getName();
                            // 把文件的名称设置唯一值,uuid
                            String uuid = UUID.randomUUID().toString().replace("-", "");
                            filename = uuid+"_"+filename;
                            // 完成文件上传
                            item.write(new File(path,filename));
                            // 删除临时文件
                            item.delete();
                        }
                    }
            
                    return "success";
                }
            }

    2. SpringMVC传统方式的文件上传
        1. 导入jar包
            注:commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它工作时需要 commons-io 包的支持。 

            <dependency>
                <groupId>commons-fileupload</ groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.4</version>
            </dependency>

        2. 编写文件上传的JSP页面

            <h3>Springmvc文件上传</h3>
            <form action="/user/fileupload2" method="post" enctype="multipart/form-data">
                选择文件:<input type="file" name="upload" /><br/>
                <input type="submit" value="上传" />
            </form>

        3. 编写文件上传的Controller控制器

            /**
            * SpringMVC传统文件上传
            */
            @Controller
            @RequestMapping("/user")
            public class UserController {
                @RequestMapping("/fileupload2")
                public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {
                    System.out.println("springmvc文件上传...");
            
                    // 使用fileupload组件完成文件上传
                    // 上传的位置
                    String path = request.getSession().getServletContext().getRealPath("/uploads/");
                    // 判断,该路径是否存在
                    File file = new File(path);
                    if(!file.exists()){
                        // 创建该文件夹
                        file.mkdirs();
                    }
            
                    // 说明上传文件项
                    // 获取上传文件的名称
                    String filename = upload.getOriginalFilename();
                    // 把文件的名称设置唯一值,uuid
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    filename = uuid+"_"+filename;
                    // 完成文件上传
                    upload.transferTo(new File(path,filename));
            
                    return "success";
                }
            }

        4. 配置文件解析器

            <!--配置文件解析器对象,要求id名称必须是multipartResolver -->
            <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
                <property name="maxUploadSize" value="10485760" />
            </bean>

    3. SpringMVC跨服务器方式的文件上传
        前提要求:创建另一个服务器(比如图片服务器作上传图片用,端口号设置为9090)
        1. 导入jar包

            <dependency>
              <groupId>commons-fileupload</groupId>
              <artifactId>commons-fileupload</artifactId>
              <version>1.3.1</version>
            </dependency>
            <dependency>
              <groupId>commons-io</groupId>
              <artifactId>commons-io</artifactId>
              <version>2.4</version>
            </dependency>
        
            <dependency>
              <groupId>com.sun.jersey</groupId>
              <artifactId>jersey-core</artifactId>
              <version>1.18.1</version>
            </dependency>
            <dependency>
              <groupId>com.sun.jersey</groupId>
              <artifactId>jersey-client</artifactId>
              <version>1.18.1</version>
            </dependency>

        2. 编写文件上传的JSP页面

            <h3>跨服务器文件上传</h3>
            <form action="/user/fileupload3" method="post" enctype="multipart/form-data">
                选择文件:<input type="file" name="upload" /><br/>
                <input type="submit" value="上传" />
            </form>

        3. 编写文件上传的Controller控制器

            /**
            * 跨服务器文件上传
            * @return
            */
            @Controller
            @RequestMapping("/user")
            public class UserController {
                @RequestMapping("/fileupload3")
                public String fileuoload3(MultipartFile upload) throws Exception {
                    System.out.println("跨服务器文件上传...");
            
                    // 定义上传文件服务器路径
                    String path = "http://localhost:9090/uploads/";
            
                    // 说明上传文件项
                    // 获取上传文件的名称
                    String filename = upload.getOriginalFilename();
                    // 把文件的名称设置唯一值,uuid
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    filename = uuid+"_"+filename;
            
                    // 创建客户端的对象
                    Client client = Client.create();
            
                    // 和图片服务器进行连接
                    WebResource webResource = client.resource(path + filename);
            
                    // 上传文件
                    webResource.put(upload.getBytes());
            
                    return "success";
                }
            }

        4. 配置文件解析器

            <!--配置文件解析器对象,要求id名称必须是multipartResolver -->
            <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
                <property name="maxUploadSize" value="10485760" />
            </bean>


## Spring中的异常处理
    1. 异常
        * 预期异常:通过捕获异常从而获取异常信息
        * 运行时异常:要通过规范代码开发、测试通过手段减少运行时异常的发生。

    2. SpringMVC处理异常思路
        * 系统Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进行异常的处理。 
    
    3. 处理异常的步骤
        1. 编写异常类(继承Exception类)和错误页面

            /**
             * 自定义异常类
             */
            public class SysException extends Exception{
                // 存储提示信息的
                private String message;
            
                public String getMessage() {
                    return message;
                }
            
                public void setMessage(String message) {
                    this.message = message;
                }
            
                public SysException(String message) {
                    this.message = message;
                }
            
            }


        2. 自定义异常处理器(实现HandlerExceptionResolver)

            /**
             * 异常处理器
             */
            public class SysExceptionResolver implements HandlerExceptionResolver{
            
                /**
                 * 处理异常业务逻辑
                 * @param request
                 * @param response
                 * @param handler
                 * @param ex
                 * @return
                 */
                public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
                    // 获取到异常对象
                    SysException e = null;
                    if(ex instanceof SysException){
                        //属于自定义的异常,即预期异常
                        e = (SysException)ex;
                    }else{
                        //不属于预期异常
                        e = new SysException("系统正在维护....");
                    }
                    // 创建ModelAndView对象
                    ModelAndView mv = new ModelAndView();
                    mv.addObject("errorMsg",e.getMessage());
                    mv.setViewName("error");
                    return mv;
                }
            }

        3. jsp页面和模拟异常
            jsp页面代码:

            <%@ page contentType="text/html;charset=UTF-8" language="java" %>
            <html>
            <head>
                <title>Title</title>
            </head>
            <body>
                <h3>异常处理</h3>
                <a href="user/testException" >异常处理</a>
            </body>
            </html>


            控制器代码:

            @Controller
            @RequestMapping("/user")
            public class UserController {
                @RequestMapping("/testException")
                public String testException() throws SysException{
                    System.out.println("testException执行了...");
            
                    try {
                        // 模拟异常
                        int a = 10/0;
                    } catch (Exception e) {
                        // 打印异常信息
                        e.printStackTrace();
                        // 抛出自定义异常信息
                        throw new SysException("查询所有用户出现错误了...");
                    }
                    return "success";
                }
            
            }

        4. 在springmvc.xml中配置异常处理器

            <!-- 配置自定义异常处理器 --> 
            <bean id="handlerExceptionResolver"  class="全限定类名"/> 

 

## SpringMVC的拦截器
    1. 拦截器的概述
        1. SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。 
        2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链 中的拦截器会按着定义的顺序执行。 
        3. 拦截器也是AOP思想的一种实现方式 
        4. 想要自定义拦截器,需要实现HandlerInterceptor接口。 
    
    
    2. 拦截器与过滤器的区别
        1. 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术,而拦截器是SpringMVC框架独有的。 
        2. 过滤器配置了/*,可以拦截任何资源,而拦截器只会对控制器中的方法进行拦截。 
    
    3. 自定义拦截器的步骤
        1. 编写一个普通的类实现HandlerInterceptor接口,并重写preHandle、postHandle、afterCompletion方法

            public class HandlerInterceptorDemo1 implements HandlerInterceptor { 
                @Override  
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {   
                    System.out.println("preHandle 拦截器拦截了");   
                    return true;  
                }
                @Override  
                public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {   
                    System.out.println("postHandle 方法执行了");  
                } 
                @Override  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {   
                System.out.println("afterCompletion 方法执行了");  
                } 
            }

        2. 在springmvc.xml中配置拦截器

            <mvc:interceptors>  
                <mvc:interceptor>   
                    <!-- 要拦截的具体的方法 -->
                    <mvc:mapping path="/**"/>
                    <!--不要拦截的方法 -->
                    <mvc:exclude-mapping path="方法路劲"/>
                    <!--配置拦截器对象-->
                    <bean id="handlerInterceptorDemo1" class="com.lyp.web.interceptor.HandlerInterceptorDemo1" />
                </mvc:interceptor> 
            </mvc:interceptors>

    4. 拦截器的方法说明

        public interface HandlerInterceptor {
            //如何调用:按拦截器定义顺序调用
            //何时调用:只有配置了都会调用
            //作用:
                如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去行处理,则返回 true。 
                如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。 
            default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
        
                return true;
            }
        
            //如何调用:按拦截器定义逆序调用
            //何时调用:在拦截器链内所有拦截器返成功调用 
            //作用:在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求 request 进行处理
            default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                    @Nullable ModelAndView modelAndView) throws Exception {
            }
        
            //如何调用:按拦截器定义逆序调用
            //何时调用:只有 preHandle 返回 true 才调用 
            //作用:在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作
            default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                    @Nullable Exception ex) throws Exception {
            }
        
        }

    5. 拦截器简单案例:验证用户是否登录
        * 需求:若用户访问登录页面,则放行;若用户已经登录,则放行;若用户还没登录并且不是访问登录页面,则不放行;

        控制器中的代码:

        //登陆页面 
        @RequestMapping("/login") 
        public String login(Model model)throws Exception{   
            return "login"; 
        }   
        //登陆提交 
        //userid:用户账号,pwd:密码 
        @RequestMapping("/loginsubmit") 
        public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{ 
            session.setAttribute("activeUser", userid);  
            return "redirect:/main.jsp";
        } 
        //退出 
        @RequestMapping("/logout") 
        public String logout(HttpSession session)throws Exception{    
            //session 过期  
            session.invalidate();     
            return "redirect:index.jsp"; 
        }

        拦截器中的代码:

        public class LoginInterceptor implements HandlerInterceptor{ 
         
            @Override  
            Public boolean preHandle(HttpServletRequest request,    HttpServletResponse response, Object handler) throws Exception { 
                //如果是登录页面则放行   
                if(request.getRequestURI().indexOf("login.action")>=0){    
                    return true;   
                }
                
                //如果用户已登录也放行
                HttpSession session = request.getSession(); 
                if(session.getAttribute("user")!=null){    
                    return true;   
                }
            
                //用户没有登录跳转到登录页面   
                request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);   
                return false;  
            } 
        } 


        


版权声明:本文为beyong_78439原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。