处理器方法的返回值

空~2022年9月15日
  • SpringMVC
大约 8 分钟

处理器方法的返回值

返回 ModelAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。

当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。

在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。

@Controller
public class UserController {
// 处理登录的控制器
	@RequestMapping(value = "islogin", method = RequestMethod.POST)
	public ModelAndView checkLogin(Users users) {
		ModelAndView mav = new ModelAndView();
		if (users.getLoginname().equals("lisi")
				&& users.getLoginpwd().equals("123")) {
			users.setRealname("李四");
			// 返回的数据
			mav.addObject("users", users);
			// 跳转的页面 配合视图解析器
			mav.setViewName("show");
		} else {
			mav.addObject("msg", "用户名或者密码错误");
			// 跳转的页面
			mav.setViewName("login");
		}
		return mav;
	}
}

ModelAndView 也可以当作方法参数传入, 不用自己创建, Model 可以单独使用.

@Controller
public class UserController {
// 处理登录的控制器
	@RequestMapping(value = "islogin", method = RequestMethod.POST)
	public ModelAndView checkLogin(ModelAndView mav, Users users) {
        Model m = new Model();
		if (users.getLoginname().equals("lisi")
				&& users.getLoginpwd().equals("123")) {
			users.setRealname("李四");
			// 返回的数据
			mav.addObject("users", users);
			// 跳转的页面 配合视图解析器
			mav.setViewName("show");
		} else {
			mav.addObject("msg", "用户名或者密码错误");
			// 跳转的页面
			mav.setViewName("login");
		}
		return mav;
	}
}

返回 String

String: 表示视图,可以是逻辑名称,也可以是完整视图路径

逻辑名称:

/** 处理器方法返回String--表示逻辑视图名称,需要配置视图解析器 */
@RequestMapping(value = "/receiveproperty.do")
public String doSome(String name, Integer age) {
    System.out.println("doSome, name=" + name + "   age=" + age);
    // show : 逻辑视图名称,项目中配置了视图解析器
    // 框架对视图执行forward转发操作
    return "show";
}

手动添加数据:

/** 处理器方法返回String--表示逻辑视图名称,需要配置视图解析器 */
@RequestMapping(value = "/receiveproperty.do")
public String doSome(String name, Integer age) {
    System.out.println("doSome, name=" + name + "   age=" + age);
    //可以自己手动添加数据到request作用域
    request.setAttribute("myname",name);
    request.setAttribute("myage",age);
    // show : 逻辑视图名称,项目中配置了视图解析器
    // 框架对视图执行forward转发操作
    return "show";
}

完整视图路径:

//处理器方法返回String,表示完整视图路径, 此时不能配置视图解析器
@RequestMapping(value = "/receiveproperty.do")
public String doSome(String name, Integer age) {
    System.out.println("doSome, name=" + name + "   age=" + age);
    request.setAttribute("myname",name);
    request.setAttribute("myage",age);
    // 完整视图路径,项目中不能配置视图解析器
    // 框架对视图执行forward转发操作
    return "/WEB-INF/view/show.jsp";
    // 如果使用了视图解析器, 那路径就会变成
    // /WEB-INF/view/WEB-INF/view/show.jsp.jsp
}

返回 void

void: 不能表示数据,也不能表示视图。

在处理 ajax 的时候,可以使用 void 返回值。 通过 HttpServletResponse 输出数据。响应 ajax 请求。

ajax 请求服务器端返回的就是数据, 和视图无关。

//处理器方法返回void, 响应ajax请求
//手动实现ajax,json数据: 代码有重复 1. java对象转为json; 2. 通过HttpServletResponse输出json数据
@RequestMapping(value = "/returnVoid-ajax.do")
public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
    System.out.println("===doReturnVoidAjax====, name="+name+"   age="+age);
    //处理ajax, 使用json做数据的格式
    //使用Student表示处理结果
    Student student  = new Student();
    student.setName("张飞同学");
    student.setAge(28);

    String json = "";
    //把结果的对象转为json格式的数据(此处使用了一个json相关依赖, 类似依赖很多)
    if( student != null){
        ObjectMapper om  = new ObjectMapper();
        json  = om.writeValueAsString(student);
        System.out.println("student转换的json===="+json);
    }
    //输出数据,响应ajax的请求
    response.setContentType("application/json;charset=utf-8");
    PrintWriter pw  = response.getWriter();
    pw.println(json);
    pw.flush();
    pw.close();
}

ajax 请求:

$(function () {
  $("button").click(function () {
    $.ajax({
      url: "returnVoid-ajax.do",
      data: {
        name: "zhangsan",
        age: 20,
      },
      type: "post",
      dataType: "json",
      success: function (resp) {
        // resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
        // jquery会把字符串转为json对象, 赋值给resp形参。
        // [{"name":"李四同学","age":20},{"name":"张三","age":28}]
        alert(resp.name + "    " + resp.age);
      },
    });
  });
});

返回对象 Object

Object:例如 String, Integer, Map, List, Student 等等都是对象

对象有属性, 属性就是数据。 所以返回 Object 表示数据, 和视图无关。

可以使用对象表示的数据,响应 ajax 请求。

现在做 ajax, 主要使用 json 的数据格式

实现步骤:

  1. 加入处理 json 的工具库的依赖, springmvc 默认使用的 jackson。

  2. 在 sprigmvc 配置文件之间加入 <mvc:annotation-driven> 注解驱动;

    对应的功能 json = om.writeValueAsString(student);

  3. 在处理器方法的上面加入 @ResponseBody 注解

对应的功能:

response.setContentType("application/json;charset=utf-8");
PrintWriter pw  = response.getWriter();
pw.println(json);

springmvc 处理器方法返回 Object, 可以转为 json 输出到浏览器,响应 ajax 的内部原理

mvc:annotation-driven 注解

mvc:annotation-driven 注解驱动实现的功能是完成 java 对象到 json,xml, text,二进制等数据格式的转换。

mvc:annotation-driven 在加入到 springmvc 配置文件后,会自动创建 HttpMessageConverter 接口的 7 个实现类对象, 包括 MappingJackson2HttpMessageConverter (使用 jackson 工具库中的 ObjectMapper 实现 java 对象转为 json 字符串)

HttpMessageConverter 接口

消息转换器。

  • 功能:定义了 java 转为 json,xml 等数据格式的方法。 这个接口有很多的实现类。

  • 这些实现类完成 java 对象到 json, java 对象到 xml,java 对象到二进制数据的转换

  • 下面的两个方法是控制器类把结果输出给浏览器时使用的:

    • boolean canWrite(Class<?> var1, @Nullable MediaType var2);
    • ``void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)`

例如下面程序

@RequestMapping(value = "/returnString.do")
@ResponseBody
public Student doReturnView2(HttpServletRequest request,String name, Integer age){
  Student student = new Student();
  student.setName("lisi");
  student.setAge(20);
  return student;
}

HttpMessageConverter 接口的 canWrite 方法作用是检查处理器方法的返回值能不能转为 var2 表示的数据格式。

检查 student (lisi,20) 能不能转为 var2 表示的数据格式。如果检查能转为 json,canWrite 返回 true

参数 MediaType:表示数据格式, 例如 json, xml 等等

write 方法:把处理器方法的返回值对象,调用 jackson 中的 ObjectMapper 转为 json 字符串。

json  = om.writeValueAsString(student);
  • @ResponseBody 注解

放在处理器方法的上面, 通过 HttpServletResponse 输出数据,响应 ajax 请求。

PrintWriter pw  = response.getWriter();
pw.println(json);
pw.flush();
pw.close();

没有加入注解驱动标签 <mvc:annotation-driven /> 时的状态:

org.springframework.http.converter.ByteArrayHttpMessageConverter

org.springframework.http.converter.StringHttpMessageConverter

org.springframework.http.converter.xml.SourceHttpMessageConverter

org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter

加入注解驱动标签时 <mvc:annotation-driven /> 的状态:

org.springframework.http.converter.ByteArrayHttpMessageConverter

org.springframework.http.converter.StringHttpMessageConverter

org.springframework.http.converter.ResourceHttpMessageConverter

org.springframework.http.converter.ResourceRegionHttpMessageConverter

org.springframework.http.converter.xml.SourceHttpMessageConverter

org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter

org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

处理器方法返回一个 Student:

通过框架转为 json,响应 ajax 请求

@ResponseBody:

作用:

把处理器方法返回对象转为 json 后,通过 HttpServletResponse 输出给浏览器。

位置:

方法的定义上面。 和其它注解没有顺序的关系。

返回对象框架的处理流程:

  1. 框架会把返回 Student 类型,调用框架的中 ArrayList<HttpMessageConverter> 中每个类的 canWrite () 方法检查那个 HttpMessageConverter 接口的实现类能处理 Student 类型的数据 --MappingJackson2HttpMessageConverter

  2. 框架会调用实现类的 write (),MappingJackson2HttpMessageConverter 的 write () 方法把李四同学的 student 对象转为 json,调用 Jackson 的 ObjectMapper 实现转为 json

    contentType: application/json;charset=utf-8

  3. 框架会调用 @ResponseBody 把 2 的结果数据输出到浏览器, ajax 请求处理完成

@RequestMapping(value = "/returnStudentJson.do")
@ResponseBody
public Student doStudentJsonObject(String name, Integer age) {
 //Student对象表示结果数据
 Student student = new Student();
 student.setName("李四同学");
 student.setAge(20);
 return student; // 会被框架转为json
}

处理器方法返回 List<Student>:

返回对象框架的处理流程:

  1. 框架会返回 List<Student> 类型,调用框架的中 ArrayList<HttpMessageConverter> 中每个类的 canWrite () 方法

    检查那个 HttpMessageConverter 接口的实现类能处理 Student 类型的数据 --MappingJackson2HttpMessageConverter

  2. 框架会调用实现类的 write(), MappingJackson2HttpMessageConverter 的 write () 方法

    把李四同学的 student 对象转为 json, 调用 Jackson 的 ObjectMapper 实现转为 json array

    contentType: application/json;charset=utf-8

  3. 框架会调用 @ResponseBody 把 2 的结果数据输出到浏览器, ajax 请求处理完成

@RequestMapping(value = "/returnStudentJsonArray.do")
@ResponseBody
public List<Student> doStudentJsonObjectArray(String name, Integer age) {
List<Student> list = new ArrayList<>();
// Student 对象表示结果数据
Student student = new Student();
student.setName("李四同学");
student.setAge(20);
list.add(student);
student = new Student();
student.setName("张三");
student.setAge(28);
list.add(student);
return list;
}

ajax 接收:

$(function () {
  $("button").click(function () {
    $.ajax({
      url: "returnStudentJsonArray.do",
      data: {
        name: "zhangsan",
        age: 20,
      },
      type: "post",
      dataType: "json",
      success: function (resp) {
        // list集合在前端会显示为一个数组
        // [{"name":"李四同学","age":20},{"name":"张三","age":28}]
        // 循环遍历
        $.each(resp, function (i, n) {
          alert(n.name + "   " + n.age);
        });
      },
    });
  });
});

处理器方法返回 String:

处理器方法返回的是 String , String 表示数据的,不是视图。

区分返回值 String 是数据,还是视图,看有没有 @ResponseBody 注解

如果有 @ResponseBody 注解,返回 String 就是数据,反之就是视图

默认使用 “text/plain;charset=ISO-8859-1” 作为 contentType, 导致中文有乱码

解决方案:给 RequestMapping 增加一个属性 produces, 使用这个属性指定新的 contentType.

返回对象框架的处理流程:

  1. 框架会把返回 String 类型,调用框架的中 ArrayList<HttpMessageConverter> 中每个类的 canWrite () 方法

    检查那个 HttpMessageConverter 接口的实现类能处理 String 类型的数据 --StringHttpMessageConverter

  2. 框架会调用实现类的 write(), StringHttpMessageConverter 的 write () 方法

    把字符按照指定的编码处理 text/plain;charset=ISO-8859-1

  3. 框架会调用 @ResponseBody 把 2 的结果数据输出到浏览器, ajax 请求处理完成

@RequestMapping(value = "/returnStringData.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(String name,Integer age){
    return "Hello SpringMVC 返回对象,表示数据";
}

ajax 接收:

$(function () {
  $("button").click(function () {
    //alert("button click");
    $.ajax({
      url: "returnStringData.do",
      data: {
        name: "zhangsan",
        age: 20,
      },
      type: "post",
      dataType: "text",
      success: function (resp) {
        alert("返回的是文本数据:" + resp);
      },
    });
  });
});