`

Spring MVC实战系列教程(3)--响应方式

 
阅读更多

     上一节讲述了spring mvc接收客户端参数传递的方式,解决了“入”的问题,本节将要讲述服务端如何生成响应到客户端,即“出”的问题。

 

     (一)直接返回一个字符串,指定视图名。

这是之前在例子中已经遇到的方式,比如这个方法返回"index", 即代表有一个包含该字符串的视图文件存在,再通过spring mvc配置文件中的视图解析器,为该视图名加上前缀和后缀,就获得了该视图在服务器中的路径和名称。即控制器执行完毕就转向执行该web应用下的/WEB-INF/jsp/index.jsp并将执行后生成的HTML作为响应发送给客户端接收

@RequestMapping("/queryString")
    public String test1(String userName,String userPass,Integer age){
        System.out.println("用户名为:"+userName+",密码为:"+userPass+"年龄为:"+age);
        return "index";
    }

<!--视图解析器-->

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

   在实际应用中, 直接跳转到一个视图通常是没有意义的,既然是MVC,那通常都要携带数据(即model)转向视图,那么怎么将所需数据对象放到响应中呢?可以在方法中添加一个Map类型的参数来保存我们model的部分,其key为String类型,value为Object类型。回想一下我们在servlet中跳转视图时是使用四个作用域对象来传递数据到视图,这四个作用域对象本质上就是一个以String为key,以Object为value的Map对象,所以spring mvc设计成Map完全合理,并且默认作用域是request,示例代码如下:

@RequestMapping("/test1")
    public String test1(Map<String, Object> resultMap){
        User user = new User();
        user.setUserName("张三");
        user.setAge(12);
       

       // 将模型数据保存在结果集中
        resultMap.put("user", user);
        return "index";
    }

注意,这里我们手工造了一个User对象,并为其赋值,实际应用中该数据对象应该来自于业务逻辑组件。那么在index.jsp中就可以使用EL来显示user对象的内容${user},当然也可以使用内嵌的java表达式<%=request.getAttribute("user")%>来打印,当然要重写下User类的toString方法,否则打印出来的就是地址。如果观察下浏览器地址栏,发现URL并没有变化,说明spring mvc默认使用服务器端跳转方式而不是响应重定向。

 

  (二)返回ModelAndView对象

    spring mvc提供了ModelAndView对象用于将视图逻辑名同模型数据部分封装在一起,这样方法可以直接返回一个封装好的ModelAndView,而不需要提供Map参数。示例如下:

@RequestMapping("/test2")
    public ModelAndView test2(){
        ModelAndView mav = new ModelAndView();
        // 设置视图逻辑名
        mav.setViewName("index");
        // 构建模型数据
        User user = new User();
        user.setUserName("李四");
        user.setAge(21);
        // 将模型加入ModelAndView实例
        mav.getModel().put("user", user);
        return mav;
    }

可以看到,其model部分依然是一个<String,Object>的Map。当然,除了单独调用其成员方法设置视图和模型以外,也可以使用ModelAndView构造器在创建对象时一次性将视图和模型绑定好,比如上面这段代码可以改成这样:

@RequestMapping("/test2")
    public ModelAndView test2() {
        // 构建模型数据
        User user = new User();
        user.setUserName("wang5");
        user.setAge(24);
        ModelAndView mav = new ModelAndView("index", "user", user);

        return mav;
    }

其效果是一样的。

 

(三)针对Ajax请求,返回普通字符串

   如果是Ajax请求,就不能返回一个视图组件了,而是单独返回一部分数据,比如最简单的就是一个字符串,请看示例代码:   @RequestMapping("/test3")
    @ResponseBody
    public String test3(String name) {
        System.out.println("参数:"+name);
        return "你的名字是:" + name;
    }

可以看出,这种方式跟之前的直接跳转视图的方法结构几乎一模一样,所不同的是为方法增加了一个叫@ResponseBody的注解,该注解表示方法的返回值(字符串)不再是一个视图名,而是直接作为响应体返回给客户端,并且不需要使用HttpServletResponse,PrintWriter这些API仅仅需要增加一个注解就非常简单得完成了一个Ajax的响应。客户端的代码如下:

<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script type="text/javascript">
    $(function(){
        //按钮单击时执行
        $("#testBtn").click(function(){
            
            $.post("${pageContext.request.contextPath}/jumpToView/test3", {name:"张三" },
                function (data){
       
                    $("#result").html(data);

                });
            });
    });
</script>
</head>
<body>
    <input type="button" id="testBtn" value="ajax请求" />
    <span id="result"></span>
</body>

 

(四)返回JSON数据

   对于简单的ajax请求,直接返回一个简单String尚可,但多数情况下需要返回对象或对象集合等复杂数据,这种情况往往使用Json串作为响应。另外,在移动互联网大行其道的今天,服务端同各种APP交互通常也使用Json。这一小节我们就来简单看看如何在spring mvc中将对象或对象集合转成json串,这里我们使用jackson框架来辅助转换过程。

   1. 在pom中添加jackson库的依赖:

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

 

   2. 将单个对象转成json,示例代码如下:

   /**
     * 使用jackson将单个对象转换为json 串返回
     * @param name
     * @return
     */
    @RequestMapping("/test4")
    @ResponseBody
    public String test4(String name,Integer age) {
        System.out.println("参数1:"+name+",参数2:"+age);
        ObjectMapper om = new ObjectMapper();
        User user = new User();
        user.setUserName(name);
        user.setAge(age);
       
        // object 转出 json串
        String jsonResult = "";
        try {
            jsonResult = om.writeValueAsString(user);
            System.out.println("转换成的Json串为:"+jsonResult);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
       
        return jsonResult;
    }

可以看到,使用jackson实现对象与json串直接的转换非常简单,只需使用一个ObjectMapper的writeValueAsString方法即可,请大家自行观察结果。

 注意:依然不要忘了@ResponseBody注解,否则spring mvc会去寻找以你的json串命名的jsp

 

3. 将对象集合转换为json串,没有什么区别,同样使用ObjectMapper的writeValueAsString方法:

/**
     * 使用jackson将对象集合转换为json 串返回
     * @param name
     * @return
     * @throws JsonProcessingException
     */
    @RequestMapping("/test5")
    @ResponseBody
    public String test5() {
        String jsonResult = "";
        List<User> userList = new ArrayList<User>();
        User u1 = new User();
        User u2 = new User();
        User u3 = new User();
       
        u1.setUserName("张三");
        u2.setUserName("李四");
        u3.setUserName("王五");
       
        userList.add(u1);
        userList.add(u2);
        userList.add(u3);
       
        ObjectMapper om = new ObjectMapper();
        try {
            jsonResult = om.writeValueAsString(userList);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
       
        return jsonResult ;
    }

 观察结果,可以看到是以json数值的形式封装的:

[{"userName":"张三","userPass":null,"age":null},{"userName":"李四","userPass":null,"age":null},{"userName":"王五","userPass":null,"age":null}]

 

(五)controller之间跳转

   跟servlet类似,除了可以跳转到一个页面,也可以从一个controller方法跳到另一个controller方法,当然要分服务器端跳转(foward)和客户端跳转(redirect)

1. forward跳转,即在同一个应用内部跳转,客户端只请求一次,在return语句中使用forward:跳转组件的URI关键字即可示例代码:

/**
     * 以foward方式从一个controller调到另一个controller
     * 并传递客户端参数和服务端参数
     * @param name
     * @return
     */
    @RequestMapping("/test6")
    public String test6(String name,HttpServletRequest req){
        req.setAttribute("key1", new String("来自于上一个controller的数据"));
        return "forward:/jumpToView/test7";
    }

 

/**
     * 跳转到的组件
     * @param name
     * @param req
     * @return
     */

@RequestMapping("/test7")
    @ResponseBody
    public String test7(String name,HttpServletRequest req){
        System.out.println(name+","+req.getAttribute("key1"));
        return "测试controller之间跳转";
    }

第一个controller方法(test6)不负责响应,把控制器交给跳转到的controller方法,跳转后第二个controller方法(test7)不仅可以接收到客户端传来的请求参数,也可以接受到在第一个controller方法中定义在request作用域中的属性值,说明两个组件都在同一个请求作用域中。观察网络报文,也确实只有一次请求和一次响应。



 

 

2. redirect跳转,请求的第一个controller方法会以302状态码响应客户端请求,通知客户端重新请求另一个组件,并另一个组件的URL放入响应头的location字段中发回给客户端。在return语句中使用redirect:跳转组件的URI关键字即可,示例代码如下:

/**
     * 以redirect方式跳转到另一个controller方法
     * @param name
     * @param req
     * @return
     */
    @RequestMapping("/test8")
    public String test8(String name,HttpServletRequest req){
        req.setAttribute("key2", new String("来自于上一个controller的数据"));
        return "redirect:/jumpToView/test7";
    }

观察结果可以发现,跳转成功了,但是第二个组件(test7)接收不到客户端传递给第一个组件(test8)的参数,也接收不到test8自己封装在request中的内部属性,观察HTTP报文,发现确实是生成了两次请求两次响应,所以两个组件实际上不在同一个请求作用域中(request)。



 

 

(六)两个额外的问题:

  1. 静态资源映射的问题:

    按照之前的配置使用spring mvc后,发现动态资源(如jsp,controller等)可以正常请求到,但是像html这种静态资源请求不到了。原因是spring mvc的核心控制器覆盖了web服务器默认对静态资源的映射,需要在springmvc相应配置文件里加入一个Handler配置即可:

<mvc:default-servlet-handler />

这个配置表示将对静态资源的处理交还给web服务器,我们这里是tomcat。

 

  2. 返回字符串响应时的中文乱码问题

    不管是普通String,还是Json,只要响应中包含了中文,都不能正常呈现。这是由于spring mvc的转换器(converter)机制对于响应中的文本类型默认采用ISO-8859-1编码,要解决此问题也很简单,在spring mvc配置文件中加入一个converter,指定响应的编码方式为UTF-8即可:

<mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!-- <value>application/json;charset=UTF-8</value> -->
                        <value>text/html;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

 

 关于converter的机制我们后面会介绍。

 

(七)总结

   本节介绍了spring mvc生成响应的几种常见方式,下一节我们将介绍使用servlet3.x的特性实现0配置加载spring mvc容器的方式。

  • 大小: 3.1 KB
  • 大小: 5.8 KB
分享到:
评论
1 楼 V转身 2017-01-17  
[color=orange][/color]
[size=x-small][/size]

相关推荐

Global site tag (gtag.js) - Google Analytics