欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-13 22:53:30
...

     上一节讲述了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作用域中的属性值,说明两个组件都在同一个请求作用域中。观察网络报文,也确实只有一次请求和一次响应。


Spring MVC实战系列教程(3)--响应方式
            
    
    博客分类: spring相关  
 

 

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)。


Spring MVC实战系列教程(3)--响应方式
            
    
    博客分类: spring相关  
 

 

(六)两个额外的问题:

  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容器的方式。

  • Spring MVC实战系列教程(3)--响应方式
            
    
    博客分类: spring相关  
  • 大小: 3.1 KB
  • Spring MVC实战系列教程(3)--响应方式
            
    
    博客分类: spring相关  
  • 大小: 5.8 KB