天天看点

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

第1章 响应数据和结果视图

1.1 返回值分类

1.1.1 返回字符串

什么情况下用这种方式:(以后的开发基本上都是这样的开发方式)

开发思路:你发请求,我后台把数据查出来,我存到这个Model对象,它帮我存到request转发到页面,前端jsp从request域里面把值取出来。(以后即可用Model这个对象代替HttpServletRequest对象,达到简写的目的,再也不用像HttpServletRequest那么麻烦了...且使用Servlet原生API会时程序耦合很高,具体原因见:SpringMC 总结 01)

@RequestMapping("/testString")
    public String testString(Model model) {
        System.out.println("execute testString()...");
        //模拟从数据库中查询出User对象
        User u = new User();
        u.setUsername("JTL");
        u.setPassword("123");
        u.setAge(17);
        model.addAttribute("user", u);
        return "success";
    }
           

前端界面:通过EL表达式从request域对象中取值

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>执行成功</h3>
${user.username}
${user.password}
${user.age}
</body>
</html>
           

1.1.2 返回值是void

@RequestMapping("/testVoid")
    public void testVoid(Model model) {
        System.out.println("execute testVoid()...");
    }
           

默认情况它会去找这个路径下的与你请求路径同名的jsp文件:这样非常不好,因为你又的创建一个名为testVoid.jsp。(默认值)

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

正确方式:

p.s.区别:

请求转发:请求转发是一次请求,不用编写项目的名称。

重定向:重定向是两次请求,需要编写项目的名称。

注意:

1、你自己手动去调转发的方法,它不会再帮你去执行视图解析器。也就不会自动跳转到/WEB-INF目录下(自己配置的)去找jsp。需要你自己提供完整的目录,不会再使用视图解析器的对象(/称为组件)。

2、当你转发层写完,如果后面还有代码的话它会继续执行,如果你不想让后面代码执行,可手动加一个return;

方式1:请求转发

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        //编写请求转发的程序
        req.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(req, resp);
        return;
    }
           

方式2:重定向

1、通过request.getContextPath()获取到项目名称

2、重定向等于又发了一个新的请求,你直接发请求是不能直接请求/WEB-INF/pages里面的页面,/WEB-INF里面的东西不能直接请求,转发是可以的。你只能请求到/webapp根目录下的jsp如index.jsp

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        //重定向
        resp.sendRedirect(req.getContextPath()+"/index.jsp");
        return;
    }
           

方式3:直接会进行响应

说明:上面两种方式都是先跳到某个jsp(跳页面),最终由Tomcat服务器帮你生成html,最终帮你响应给用户。还有这样一种情况:你可能直接发请求,控制台/控制器直接通过输出流,把数据响应给浏览器。response.getWrite()拿到输出流。

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 解决响应中文乱码
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        //直接会进行响应
        resp.getWriter().print("你好!");
        return;
    }
           

1.1.3 返回值是ModelAndView对象

说明:由SpringMVC框架提供的一个对象。它也可以通过视图解析器帮你跳转到某个页面。与返回字符串那个方式的功能是一样的,查到一个JavaBean放到Model中然后返回给View视图;ModelAndView也可以存储JavaBean对象,也可存储你想往哪个页面作 跳转,与前面那个代码做的功能是一样的,只是写法有点不一样。且它底层也会把user对象存入到request域对象中,因为ModelAndView源码中就有ModelMap这个属性Model这个接口的实现类里就有:

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

特别注意:这种方式和反回字符串方式没什么区别,写法有点不一样而已。其实返回字符串这个方式,它的底层最终也会选择ModelAndView这个方式!底层源码还是用的ModelAndView这个类。

@RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        System.out.println("execute testModelAndView()...");
        //创建ModelAndView对象
        ModelAndView mv = new ModelAndView();
        //模拟从数据库中查询出User对象
        User u = new User();
        u.setUsername("JT.L");
        u.setPassword("123");
        u.setAge(17);

        // 把user对象存储到mv对象中,它底层也会把user对象存入到request域对象中
        mv.addObject("user", u);
        // 跳转到哪个页面
        mv.setViewName("success");
        return mv;
    }
           

1.2 转发和重定向

还有一种写法(SpringMVC框架提供的转发和重定向),用一些关键字来表示转发和重定向,这个方式用的比较少,且当用关键字去做转发或重定向时,它是用不了视图解析器这个对象(组件)的。

1.2.1 forward请求转发

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");
        
        // 请求的转发
        return "forward:/WEB-INF/pages/success.jsp";
    }
           

说明:Controller方法在提供了String类型的返回值之后,默认就是请求转发。我们也可以像上面例子,使用forward关键字显式进行请求转发。但是,一旦用了forward:则路径必须写成实际视图url,不能写逻辑视图,因为它用不了视图解析器。它相当于requestl.getRequestDispathcher("url").forward(request,response)。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。

补充,转发到其他控制器:

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 请求的转发
        return "forward:testModelAndView";
    }
           

说明:必须加上forward关键字,虽然Controller方法在提供了String类型的返回值之后,默认就是请求转发,但是你不加forward直接写其他控制器的RequestMapping如:testModelAndView是转发不成功的。

1.2.2 Redirect 重定向

说明:使用关键字的重定向,不需要像response.sendRedirect(url)方式通过request.getContextPath()加上项目名称。因为它底层帮你把项目名加上了,以后在用关键字作重定向的时候,不用再去加项目的名称,框架已经默认帮你加好了。

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 重定向
        return "redirect:/index.jsp";
    }
           

重定向也可以定向到其他控制器的方法:

@RequestMapping("/testForwardOrRedirect")    
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 重定向到其他控制器方法
        return "redirect:testModelAndView";
    }
           

总结对比:

1、关键字+String返回类型:请求转发或重定向到jsp或其他控制器方法的对比

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 请求的转发
        // return "forward:/WEB-INF/pages/success.jsp";
        // 请求转发到其他控制器的方法
        // return "forward:testModelAndView";

        // 重定向
        // return "redirect:/index.jsp";
        // 重定向到其他控制器方法
        return "redirect:testModelAndView";
    }
           

2、返回值void类型:另一种方式请求转发或重定向到其他控制器方法:

(一)请求转发:

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 请求转发
        req.getRequestDispatcher("testModelAndView").forward(req,resp);
        return;
    }
           

(二)重定向:

   说明:重定向其他控制器方法也不需要写项目名称,但是到其他jsp一定要加上项目名称:

​
    @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 重定向 注意这里也不需要写项目名称
        resp.sendRedirect("testModelAndView");
        return;
    }

​
           

1.3 ResponseBody响应json数据

说明:之前都是用的转发或者重定向跳转到jsp再去作响应,可能有这样一个场景:页面发送一个ajax请求,明显是一个异步请求,那我的后台需要把一些对象转换成json的字符串,然后给你响应回去。如果有这样一个需求,那么用ResponseBody这个注解就可以完成这个事。

1、先搭建一个异步的环境。

1)引入jquery.min.js

2)存在的问题:在web.xml中配置了前端控制器,拦截了:/,意思就是任何资源都会被拦截到,在jsp中引入js文件其实它也会去请求服务器中对应的那个js文件,现在就存在这样一个问题:DispatcherServlet这个前端控制器会把这些静态的资源文件给拦截了。解决方式:告诉前端控制器这些静态资源(静态资源:js、css、图片)不要去拦截;在springmvc.xml文件中配置:

请求js文件截图:(被拦截了)

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传
Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

springmvc.xml中的配置:

<!--告诉前端控制器,哪些静态资源不拦截-->
    <mvc:resources location="/js/" mapping="/js/**"/>
           

说明:location="/js/"  -- js文件夹下面的任何文件都可以不拦截

           mapping="/js/**" -- 跟你的映射请求是有关的,以后你请求路径带/js下面任何文件都会不会对它进行拦截,如:

在jsp中的请求:

<script src="js/jquery.min.js"></script>
           

在浏览器中的表现:

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

补充其他的:

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

特别注意:

jsp中js路径的两种写法:

1)相对路径方式

<script src="js/jquery.min.js"></script>
           

2)这种写法记得把isELIgnored="false"设置上,因为用了EL表达式的$符号

<script src="${pageContext.request.contextPath}/js/jquery.min.js">
           

2、编写ajax请求

<script>
        // 页面加载,绑定单击事件
        $(function () {
            //#btn id选择器
            $("#btn").click(function () {
                // alert("hello btn");
                // 发送ajax请求
                $.ajax({
                    // 编写json格式,设置属性和值
                    url:"user/testAjax",
                    //json的maime类型???
                    contentType:"application/json;charset=UTF-8",
                    // ''可以往里面再做嵌套 json键值对
                    data:'{"username":"JTL","password":"123","age":"17"}',
                    dataType:"json",
                    type:"post",
                    success:function (data) {
                        // data服务器端响应的json数据,进行解析
                        alert(data);// 会弹出一个对象,因为返回的是一个json的对象
                        // 解析这个对象 取属性
                        alert(data.username);
                        alert(data.password);
                        alert(data.age);
                    }
                })
            })
        })
    </script>
           

3、把发过来的json封装到一个JavaBean的对象当中

说明:这个事非常好做,SpringMVC的框架已经帮我们做好了。你发过来的是一串Json的字符串,我能拿到,如果你发的串的key值跟我JavaBean里面的属性名是相同的,那么框架可以帮你把串直接封装到对象当中。但是在做这个转换的时候,把这个key封装到对象的时候,需要用到以下Jar包。这个Jar的作用:把串转成对象;或者将对象转换成Json字符串。

<!-- json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包 -->
        <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>
           

有了以上的Jar包之后,后端是直接能把前端传过来的Json数据封装到JavaBean对象当中去,它是一个自动的过程!只需要你加一个@RequestBody的注解即可。

@RequestMapping("/testAjax")
    public void testAjax(@RequestBody User user) {
        System.out.println("execute testAjax()...");
        // 客户端发送ajax的请求,传的是json字符串,后端(自动)把json字符串封装到user对象中了
        System.out.println(user);
    }
           

控制台输出结果:

Spring MVC总结 02 第1章 响应数据和结果视图第2章 SpringMVC实现文件上传

进一步编写Controller方法:

说明:

@RequestBody:接收的

@ResponseBody:响应的,返回(return user)的时候直接讲user对象转成一个json的字符串,转完了直接帮你响应

@RequestMapping("/testAjax")
    public @ResponseBody User testAjax(@RequestBody User user) {
        System.out.println("execute testAjax()...");
        // 客户端发送ajax的请求,传的是json字符串,后端(自动)把json字符串封装到user对象中了(你只需要加一个@RequestBody注解)
        System.out.println(user);
        // 做响应,模拟查询数据库
        user.setUsername("JT.L");
        user.setAge(18);
        // 做响应(查出来跟数据库不一样) -- MVC框架已经把事全做好了,只需要返回一个User对象即可
        // 但是你返回的是一个对象,但是最终给前端的还是一个json数据,因为设置了返回的数据类型dataType:json
        // 反对象不行得把对象转成json,但也不用你自己转,只需要加一个@ResponseBody注解
        return user;
    }
           

总结:整个的发送请求获得响应的过程都完成了,这就是以后用异步发送json数据开发方式。

第2章 SpringMVC实现文件上传

2.1 文件上传的回顾

2.1.1 文件上传的必要前提

1、form表单的enctype取值必须是:multipart/form-data(指定传输数据为二进制数据,例如图片、mp3、文件。http请求中的multipart/form-data,会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。 既可以上传键值对,也可以上传文件)

默认值:application/x-www-form-urlencoded(会将表单内的数据转换为键值对)

2、method属性取值必须是post:如果是get会把你请求的东西放在地址栏上,地址栏的大小是有限制的,装不了多少数据;大数据的提交必须选post。

3、提供一个文件选择域<input type=”file” /> 

2.1.2 文件上传的原理分析

当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。

enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:key=value&key=value&key=value 

当form表单的enctype 取值为Mutilpart/form-data时,请求正文内容就变成:

每一部分都是 MIME 类型描述的正文,如下:

-----------------------------7de1a433602ac   分界符 
Content-Disposition: form-data; name="userName"  协议头 
  
aaa              协议的正文 
-----------------------------7de1a433602ac 
Content-Disposition: form-data; name="file"; 
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt" 
Content-Type: text/plain         协议的类型(MIME 类型) 
 
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 
-----------------------------7de1a433602ac-- 
           

2.1.3 借助第三方组件实现文件上传

这些组件给我们提供了api,可以很方面的解析上传文件的请求体,不需要我们过于细节的去解析那个请求体(不需要我们知道请求体的格式也能正确解析)。这个组件是Apache提供的。

注:剩余内容见另一个博客

继续阅读