Spring MVC 学习总结(三)——请求处理方法Action详解

Spring MVC 学习总结(三)——请求处理方法Action详解

Spring MVC中每个控制器中可以定义多个请求处理方法,我们把这种请求处理方法简称为Action,每个请求处理方法可以有多个不同的参数,以及一个多种类型的返回结果。

一、Action参数类型

1.1、自动参数映射

1.1.1、基本数据类型

方法的参数可以是任意基本数据类型,如果方法参数名与http中请求的参数名称相同时会进行自动映射,视图user目录下的index.jsp与示例代码如下:

package cn.liuw.controller;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

@RequestMapping("/user")

public class UserController {

// 自动参数映射

@RequestMapping("/index")

public String index(Model model, int id, String name) {

model.addAttribute("message", "name=" + name + ",id=" + id);

return "user/index";

}

}

<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

User

${message}

运行结果如下:

1.1.2、自定义数据类型

除了基本数据类型,也可以自定义的数据类型,如一个自定义的POJO对象,Spring MVC会通过反射把请中的参数设置到对象中,转换类型,示例代码如下:

package cn.liuw.controller;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import cn.liuw.domain.User;

@Controller

@RequestMapping("/user")

public class UserController {

// 自动参数映射自定义数据类型

@RequestMapping("/idx")

public String index(Model model, User user) {

model.addAttribute("message", user);

return "user/index";

}

}

package cn.liuw.domain;

public class User {

private int id;

private String name;

public User() {

}

public User(int id, String name) {

super();

this.id = id;

this.name = name;

}

@Override

public String toString() {

return "User [id=" + id + ", name=" + name + "]";

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

运行结果:

示例中使用的是的URL中的参数,其实也可以是客户端提交的任意参数,特别是表单中的数据。

1.1.3、复杂数据类型

这里指的复杂数据类型指的是一个自定义类型中还包含另外一个对象类型,如用户类型中包含产品对象:

package cn.liuw.domain;

public class User {

private int id;

private String name;

private Product product;

public User() {

}

public User(int id, String name) {

super();

this.id = id;

this.name = name;

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Product getProduct() {

return product;

}

public void setProduct(Product product) {

this.product = product;

}

@Override

public String toString() {

return "User [id=" + id + ", name=" + name + "]";

}

}

class Product {

private int pid;

private String pname;

public int getPid() {

return pid;

}

public void setPid(int pid) {

this.pid = pid;

}

public String getPname() {

return pname;

}

public void setPname(String pname) {

this.pname = pname;

}

@Override

public String toString() {

return "Product [pid=" + pid + ", pname=" + pname + "]";

}

}

复杂数据类型映射示例:

@RequestMapping("/idx2")

public String index2(Model model, User user) {

model.addAttribute("message", user.getName()+","+user.getProduct().getPname());

return "user/index";

}

运行结果:

为了方便这里我使用的是url,这里当然可以是一个表单,如下代码所示:

username:

pdctname:

1.1.4、数组

浏览器请求传递参数:http://localhost:8080/user/idx01?ids=1&ids=2&ids=3

第一种:

代码中用数组Integer[] ids接收,如下:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/idx01")

public String index2(Model model, Integer[] ids) {

model.addAttribute("message", Arrays.toString(ids));

return "user/index";

}

}

第二种:

用集合List id接收,但是List集合必须得用其他类包装起来。如下:

包装类:

public class User {

private int id;

private String name;

List ids;

//getter and setter

}

代码类:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/idx02")

public String index2(Model model, User user) {

model.addAttribute("message", user);

return "user/index";

}

}

以上两种浏览器url传参一样,接收不一样。

1.1.5、List集合类型

不能直接在index2()方法的参数中指定List类型,定义一个类型包装List集合在其中。这里Controller中接收代码与数组类型的第二种方法一样,但是,传参数不一样。

浏览器请求传递参数:http://localhost:8080/user/idx01?ids[0]=1&ids[1]=2&ids[2]=3

这里同样可以使用一个表单向服务器提交数据,不过TomCat高版本可能会产生错误。因为get请求新规范规定不允许url带特殊字符,解决方法:对请求编码解码(UrlDecode、UrlEncode)。

1.1.6、Map集合类型

Map集合与List集合类似,也算封装在一个类中,请求参数方式也一样。

1.2、@RequestParam参数绑定

简单的参数可以使用上一节中讲过的自动参数映射,复杂一些的需使用@RequestParam完成,虽然自动参数映射很方便,但有些细节是不能处理的,如参数是否为必须参数,名称没有办法指定,参数的默认值就没有有办法做到了。如果使用@RequestParam可以实现请求参数绑定,Spring MVC会自动查找请求中的参数转类型并将与参数进行绑定,示例代码如下:

1.2.1、基本数据类型绑定与注解属性

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/index")

public String index(Model model, @RequestParam(required = false, defaultValue = "99") int id) {

model.addAttribute("message", id);

return "user/index";

}

}

@RequestParam共有4个注解属性,required属性表示是否为必须,默认值为true,如果请求中没有指定的参数会报异常;defaultValue用于设置参数的默认值,如果不指定值则使用默认值,只能是String类型的。name与value互为别名关系用于指定参数名称。

运行结果:

1.2.2、List与数组绑定基本数据类型

在上一节中我们使用自动参数映射是不能直接完成List与数组绑定的,结合@RequestParam可以轻松实现,示例代码如下所示:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/index1")

public String index1(Model model, @RequestParam(name = "u") List usernames) {

model.addAttribute("message", usernames.get(0)+","+usernames.get(1));

return "user/index";

}

}

运行结果:

直接在URL中输入测试数据可以绑定成功,使用表单同样可行,页面脚本如下:

user/form.jsp

<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

User

阅读

上网

电游

user/index.jsp

<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

User

${message}

请求处理方法代码如下:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/form")

public String form(){

return "user/form";

}

@RequestMapping("/index1")

public String index1(Model model, @RequestParam(name = "u") List usernames) {

model.addAttribute("message", usernames.get(0)+","+usernames.get(1));

return "user/index";

}

}

运行结果:

@RequestParam("u")是必须的,因为页面中的表单name的名称为u,所有服务器在收集数据时应该使用u而非usernames,如果同名则可以省去。

1.2.3、@RequestBody

@RequestBody 注解将HTTP请求正文插入方法中,使用适合的 HttpMessageConverter将请求体写入某个对象。作用: 1) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上; 2) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

使用时机:

A) GET、POST方式提时, 根据request header Content-Type的值来判断:

application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理); multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据); 其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);

B) PUT方式提交时, 根据request header Content-Type的值来判断:

application/x-www-form-urlencoded, 必须;multipart/form-data, 不能处理;其他格式, 必须;

说明:request的body部分的数据编码格式由header部分的Content-Type指定;

例如:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/login")

@ResponseBody

//将ajax(datas)发出的请求写入 User对象中

public User login(@RequestBody User user) {

return user;

}

}

@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上。

$.ajax({

url:"/login",

type:"POST",

data:'{"userName":"admin","pwd","admin123"}',

content-type:"application/json charset=utf-8",

success:function(data){

alert("request success ! ");

}

});

当然,也可以将其分别绑定到对应的字符串上。

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/index")

@ResponseBody

public void index(@RequestBody String username,@RequestBody String password) {

System.out.println(username+","+password);

}

}

需要注意的是,JSON字符串中的key必须对应user中的属性名。

1.2.4、List与数组直接绑定自定义数据类型与AJAX

上一小节中我们绑定的集合中存放的只是基本数据类型,如果需要直接绑定更加复杂的数据类型则需要使用@RequestBody与@ResponseBody注解了,先解释一下他们的作用:

@RequestBody 将HTTP请求正文转换为适合的HttpMessageConverter对象。 @ResponseBody 将内容或对象作为 HTTP 响应正文返回,并调用适合HttpMessageConverter的Adapter转换对象,写入输出流。

@RequestBody默认接收的Content-Type是application/json,因此发送POST请求时需要设置请求报文头信息,否则Spring MVC在解析集合请求参数时不会自动的转换成JSON数据再解析成相应的集合,Spring默认的json协议解析由Jackson完成。要完成这个功能还需要修改配置环境,具体要求如下:

a)、修改Spring MVC配置文件,启用mvc注解驱动功能,

b)、pom.xml,添加jackson依赖,添加依赖的配置内容如下:

com.fasterxml.jackson.core

jackson-databind

2.7.4

com.fasterxml.jackson.core

jackson-core

2.7.4

com.fasterxml.jackson.core

jackson-annotations

2.7.4

c)、ajax请求时需要设置属性dataType 为 json,contentType 为 'application/json;charse=UTF-8',data 转换成JSON字符串,如果条件不满足有可能会出现415异常。

示例代码如下:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/action")

public void action(@RequestBody List users,

HttpServletResponse response) throws IOException{

response.setCharacterEncoding("utf-8");

response.getWriter().write("添加成功!");

}

}

action方法的参数@RequestBody List users是接收从客户端发送到服务器的User集合。在参数前增加@RequestBody的作用是让Spring MVC在收到客户端请求时将选择合适的转换器将参数转换成相应的对象。默认的请求内容并非是application/json,而是application/x-www-form-urlencoded。因此,在客户端请求时设置内容类型为application/json。

我们写一个jsp页面form1.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

List与数组直接绑定自定义数据类型与AJAX

页面中的方法是实现将一个json集合发送到服务器并映射成一个List集合。

1.3、重定向与转发

重定向:

@Controller

@RequestMapping("/user")

public class UserController {

// 重定向

@RequestMapping("/index")

public String index(Model model) {

return "user/index";

}

@RequestMapping("/login")

public String login(Model model) {

model.addAttribute("message", "login-name");

return "redirect:index";

}

}

转发:

@Controller

@RequestMapping("/user")

public class UserController {

//转发

@RequestMapping("/index")

public String index(Model model){

return "user/index";

}

@RequestMapping("/logout")

public String logout(Model model){

model.addAttribute("message","logout-status");

return "forward:index";

}

}

重定向和转发区别:

1.重定向是客户端重新发的请求,转发是服务端的。

2.重定向URL变化,转发URL不变。

3.我们看到重定向和转发的运行结果,请求是Request域,重定向只会在第二次请求中携带Model中的参数。

二、Action返回值类型

ModelAndView Model Map 包含模型的属性 View String 视图名称 void HttpServletResponse HttpEntity或ResponseEntity HttpHeaders Callable DeferredResult ListenableFuture ResponseBodyEmitter SseEmitter StreamingResponseBody 其它任意类型,Spring将其视作输出给View的对象模型

2.1、视图中url问题

代码如下:

view目录下的hello/sayHello.jsp视图,内容如下:

我们看到jsp中图片的路径为:/static/img/zhongqiu.jpg。因为我们讲静态资源(css,js,img等)放在了WEB-INF下,所以MVC要对静态文件路径做处理,如下:

运行结果:

或者也可以用另外一种方式。因为静态资源一般不需要放到WEB-INF下,所以可以将静态资源移动到webapp目录下。此时,我们需要修改MVC处理静态文件的方式。

配置,MVC在对请求处理的时候,如果是静态文件请求,那么不经过DispatchServlet,而是以webapp为根路径去查找对应静态资源。否则,让DispatchServlet处理。

此时,我们的JSP视图中,图片的路径应写成相对于webapp目录的"绝对路径"。我们使用JSTL中C标签的可以做到。

2.2.2、String作为内容输出

如果方法声明了注解@ResponseBody ,将内容或对象作为 HTTP 响应正文返回,并调用适合HttpMessageConverter的Adapter转换对象,写入输出流。此时,返回值String不再是JSP路径而是响应内容,示例如下:

@Controller

@RequestMapping("/bye")

public class ByeController {

@RequestMapping("")

@ResponseBody

public String index(Model model) {

StringBuilder sb = new StringBuilder("

你好

");

sb.append("\n

是的,今天很不开森!想骂人。

");

return sb.toString();

}

}

此时,运行出现了乱码!因为,@ResponseBody 将内容或对象作为 HTTP 响应正文返回,并调用适合HttpMessageConverter的Adapter转换对象,写入输出流。其中有7种转换器,StringHttpMessageConverter转换器默认转换字符集为:ISO-8859-1。此时,我们需要修改springmvc.xml配置文件:

运行结果:

2.3、返回值为void

void在普通方法中是没有返回值的意思,但作为请求处理方法并非这样,存在如下两种情况:

2.3.1、方法名默认作为视图名

当方法没有返回值时,方法中并未指定视图的名称,则默认视图的名称为方法名,如下代码所示:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/index")

public void index(Model model) {

}

}

直接会去访问的路径是:url=/WEB-INF/views/user/index.jsp。可见URL= prefix前缀+控制器路径+方法名称 +suffix后缀组成。

2.3.2、直接响应输出结果

当方法的返回值为void,但输出流中存在输出内容时,则不会去查找视图,而是将输入流中的内容直接响应到客户端,如下代码所示:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/hello")

public void hello(HttpServletResponse response) throws IOException {

response.setHeader("Content-type", "text/html;charset=UTF-8");

response.setCharacterEncoding("utf-8");

response.getWriter().write("

你好,水晶之夜!

");

}

}

运行结果:

2.4、返回值为ModelAndView

在旧的Spring MVC中ModelAndView使用频率非常高,它可以同时指定须返回的模型与视图对象或名称,示例代码如下:

@Controller

@RequestMapping("/user")

public class UserController {

@RequestMapping("/msg")

public ModelAndView msg()

{

//1只指定视图

//return new ModelAndView("/user/msg");

//2分别指定视图与模型

//Map model=new HashMap();

//model.put("message", "ModelAndView msg");

//return new ModelAndView("/user/msg",model);

//3同时指定视图与模型

//return new ModelAndView("/user/msg","message","英雄之战,冢之谷!");

//4分开指定视图与模型

ModelAndView modelAndView=new ModelAndView();

//指定视图名称

modelAndView.setViewName("/user/msg");

//添加模型中的对象

modelAndView.addObject("message", "

Hello ModelAndView

");

return modelAndView;

}

}

ModelAndView有多个构造方法重载,单独设置属性也很方便,运行结果如下:

2.5、返回值为Map

当返回结果为Map时,相当于只是返回了Model,并未指定具体的视图,返回视图的办法与void是一样的,即URL= prefix前缀+控制器路径+方法名称 +suffix后缀组成,示例代码如下:

@RequestMapping("/map")

public Map map()

{

Map model=new HashMap();

model.put("message", "Hello Map");

model.put("other", "more item");

return model;

}

2.6、返回值为Model类型

Model接口定义在org.springframework.ui下,model对象会用于页面渲染,视图路径使用方法名,与void类似。示例代码如下:

@RequestMapping("/party")

public Model party(Model model)

{

model.addAttribute("message", "返回类型为org.springframework.ui.Model");

return model;

}

2.7、@ResponseBody

默认情况下面Spring MVC会使用如下流程处理请求与响应结果:

@ResponseBody是作用在方法上的,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】,在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

2.8、@RestController

Spring 4 MVC中提供的@RestController,使用最少的代码来构建一个Restful Web Service,支持返回xml或json数据,这个可以让用户选择,通过URL后缀.xml或.json来完成。

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。

目前在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。例如,Amazon.com提供接近REST风格的Web服务进行图书查找;雅虎提供的Web服务也是REST风格的。

Rest风格的URL:

新增: http://localhost:8080/order POST

修改: http://localhost:8080/order/1 PUT update?id=1

获取:http://localhost:8080/order/1 GET get?id=1

删除: http://localhost:8080/order/1 DELETE delete?id=1

实现代码如下:

@RestController

@RequestMapping("/car")

public class CarController {

//查询

@GetMapping("/{id}")

public Car getCar(@PathVariable int id){

return null;

}

//删除

@DeleteMapping("/{id}")

public Car deleteCar(@PathVariable int id){

return null;

}

//新增

@PostMapping

public Car addCar(Car car){

return null;

}

//更新

@PutMapping

public Car updateCar(Car car){

return null;

}

}

2.9、小结

使用 String 作为请求处理方法的返回值类型是比较通用的方法,这样返回的逻辑视图名不会和请求 URL 绑定,具有很高的灵活性,而模型数据又可以通过Model控制。

使用void,map,Model时,返回对应的逻辑视图名称真实url为:prefix前缀+控制器路径+方法名 +suffix后缀组成。使用String,ModelAndView返回视图名称可以不受请求的url绑定,ModelAndView可以设置返回的视图名称。另外在非MVC中使用的许多办法在Action也可以使用。

三、Spring MVC乱码解决方法

3.1、页面编码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

3.2、URL中的乱码

改tomcat中server.xml中Connector的port="8080",加上一个 URIEncoding="utf-8"

3.3、配置过滤器,指定所有请求的编码

(1)配置spring的编码过滤器,为了防止spring中post方式提交的时候中文乱码。方法:修改web.xml文件,添加spring的编码过滤器。

CharacterEncodingFilter

org.springframework.web.filter.CharacterEncodingFilter

encoding

utf-8

CharacterEncodingFilter

/*

(2)配置编码过滤器,方法:先创建filter类,再修改web.xml文件,注意的是放在spring的编码过滤器之后

Filter类:

package cn.liuw.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class EncodingFilter implements Filter {

private String encoding="";

@Override

public void destroy() {

// TODO Auto-generated method stub

}

//过滤方法 是否往下执行

@Override

public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain)

throws IOException, ServletException {

HttpServletRequest request=(HttpServletRequest)arg0;

HttpServletResponse response=(HttpServletResponse)arg1;

request.setCharacterEncoding(encoding);

response.setCharacterEncoding(encoding);

//过滤通行证

chain.doFilter(request, response);

}

//根据web.xml文件的配置进行初始化

@Override

public void init(FilterConfig arg0) throws ServletException {

this.encoding = arg0.getInitParameter("Encoding");

}

}

修改web.xml,添加如下配置:

EncodingFilter

com.qiyuan.filter.EncoidingFilter

Encoding

utf-8

EncodingFilter

/*

3.4、文件编码

将文件另存为utf-8格式

3.5、数据库编码

连接字符串指定编码格式

URL=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=UTF-8

参考自:https://www.cnblogs.com/best/p/5669010.html

相关文章

如何查看web服务器的ip地址
28365备用网址官方网站

如何查看web服务器的ip地址

🕒 09-16 👁️ 9072
御神装·勿忘
365bet亚洲娱乐场

御神装·勿忘

🕒 07-14 👁️ 2214
王者荣耀演员行为最高会面临什么处罚-1月24日演员专项打击公告