405
405错误是请求方式和服务器处理方式不一致造成的
@RequestMapping(value="/test", method=RequestMethod.POST)
public String testGET() {
System.out.println("SUCCESS:GET");
return "success";
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="test">测试springMVC</a>
<br />
<form action="test" method="post">
<input type="submit" value="测试POST" />
</form>
</body>
</html>
package com.atguigu.controller;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.atguigu.bean.User;
@Controller
public class ParamController {
/*
* 在springMVC获取客户端传递的数据的方式:
* 1、在处理请求的方法中,加入相对应的形参,保证形参参数名和传递的数据的参数名保持一致,就可以自动赋值
* value:当不满足赋值条件时,可以使用value属性,指定映射关系
* required:设置形参是否必须被赋值,默认为true,必须赋值,若设置为false,则不必须赋值,因此形参的值为null
* defaultValue:若形参所获得的值为null,则设置一个默认值,用在分页和模糊查询中
*/
/*@RequestMapping(value="/param", method=RequestMethod.POST)
public String param(@RequestParam(value="username", required=false, defaultValue="1")String name, String password, String age) {
System.out.println("username="+name+",password="+password+",age="+age);
return "success";
}*/
/**
* @RequestHeader:在处理请求的方法上,获取请求头信息,用法和@RequestParam一致
*/
/*@RequestMapping(value="/param", method=RequestMethod.POST)
public String param(@RequestHeader("Accept-Language")String AcceptLanguage) {
System.out.println("AcceptLanguage="+AcceptLanguage);
return "success";
}*/
/**
* @CookieValue:在处理请求的方法上,获取cookie信息,用法和@RequestParam一致
*/
/*@RequestMapping(value="/param", method=RequestMethod.POST)
public String param(@CookieValue(value="JSESSIONID")String JSESSIONID) {
System.out.println("JSESSIONID="+JSESSIONID);
return "success";
}*/
/**
* 可以使用POJO获取客户端数据,要求实体类对象中的属性名一定要和页面中表单元素的name属性值一致,且支持级联关系
* 可以通过设置形参的方式,获取servletAPI
*/
/* @RequestMapping(value="/param", method=RequestMethod.POST)
public String param(User user, HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
//System.out.println(user);
System.out.println(username);
return "success";
}*/
/*
* springMVC处理请求过程中,往作用域中放值有以下三种方式
* 总结:根据ModelAndView源码调试,不管使用以下那种方式,最终都会把model数据和view数据封装到一个ModelAndView中
*/
/*@RequestMapping(value="/param", method=RequestMethod.POST)
public ModelAndView param() {
ModelAndView mav = new ModelAndView();
mav.addObject("username", "root");//往request作用域中放值
mav.setViewName("success");//设置视图名称,实现页面跳转
return mav;
}*/
/*@RequestMapping(value="/param", method=RequestMethod.POST)
public String param(Map<String, Object> map) {
map.put("username", "admin");//向作用域中放值
return "success";//返回视图名称
}*/
@RequestMapping(value="/param", method=RequestMethod.POST)
public String param(Model model, String username) {
System.out.println(username);
model.addAttribute("username", "张三");//向作用域中放值
return "success";//返回视图名称
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="param" method="post">
username:<input type="text" name="username" /><br />
password:<input type="text" name="password" /><br />
age:<input type="text" name="age" /><br />
province:<input type="text" name="address.province" /><br />
city:<input type="text" name="address.city" /><br />
country:<input type="text" name="address.country" /><br />
<input type="submit" value="添加" />
</form>
</body>
</html>
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class RESTController {
@RequestMapping(value="/testREST/{id}", method=RequestMethod.GET)
public String getUserById(@PathVariable("id")Integer id) {
System.out.println("GET,id="+id);
return "success";
}
@RequestMapping(value="/testREST", method=RequestMethod.POST)
public String insertUser() {
System.out.println("POST");
return "success";
}
@RequestMapping(value="/testREST", method=RequestMethod.PUT)
public String updateUser() {
System.out.println("PUT");
return "success";
}
@RequestMapping(value="/testREST/{id}", method=RequestMethod.DELETE)
public String deleteUser(@PathVariable("id") Integer id) {
System.out.println("DELETE,id="+id);
return "success";
}
@RequestMapping(value="testAjax_DELETE", method=RequestMethod.DELETE)
public void testAjax_DELETE(Integer id) {
System.out.println("testAjax_DELETE,id="+id);
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function test() {
$.ajax({
url:"testAjax_DELETE",
type:"DELETE",
data:{id:1001},
dataType:"json",
success:function (obj){
alert(obj);
}
});
}
</script>
</head>
<body>
<a href="testREST/1001">测试GET</a>
<br />
<form action="testREST" method="POST">
<input type="submit" value="测试POST" />
</form>
<br />
<form action="testREST" method="POST">
<input type="hidden" name="_method" value="PUT" />
<input type="submit" value="测试PUT" />
</form>
<br />
<form action="testREST/1001" method="POST">
<input type="hidden" name="_method" value="DELETE" />
<input type="submit" value="测试DELETE" />
</form>
<input type="button" value="测试AJAX" onclick="test()" />
</body>
</html>
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
//@RequestMapping("/mvc")
public class TestController {
/**
* @RequestMapping:设置请求映射,把请求和控制层中的方法设置映射关系
* 当请求路径和@RequestMapping的value属性值一致时,则该注解所标注的方法即为处理请求的方法
*
* @RequestMapping可以加在类上,也可以加在方法上
* 若类和方法上都加得有,应该一层一层的访问,先访问类,在访问类中的方法
*
* method:用来设置请求方式,只有客户端发送请求的方式和method的值一致,才能处理请求
* 请求方式:GET 查询 POST 添加 PUT 修改 DELETE 删除
* params:用来设置客户端传到服务器的数据,支持表达式
* username !username username=admin username!=admin
* headers:用来设置请求头信息,所发送的请求的请求头信息一定要和headers属性中所设置的一致
*/
@RequestMapping(value="/test",
/*method=RequestMethod.POST,
params= {"username","age!=12"}*/
headers= {"Accept-Language=zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"})
public String testPOST() {
System.out.println("SUCCESS:POST");
return "success";
}
@RequestMapping(value="/test", method=RequestMethod.POST)
public String testGET() {
System.out.println("SUCCESS:GET");
return "success";
}
/**
* springMVC支持Ant方式的请求路径
* 在Ant中,有3中匹配符
* *:任意字符
* ?:任意一个字符
* **:任意多层目录
*/
@RequestMapping(value="/*/ant??/**/testAnt")
public String testAnt() {
System.out.println("SUCCESS:testAnt");
return "success";
}
/**
* 以前:localhost:8080/springMVC02/testREST?id=1001&username=admin
* 现在:localhost:8080/springMVC02/testREST/1001/admin
*/
@RequestMapping("/testREST/{id}/{username}")
//@ PathVariable通过占位符获取前端传的值。赋给后面的Integer id 中的id String username 中的username
public String testREST(@PathVariable("id")Integer id, @PathVariable("username")String username) {
System.out.println("id:"+id+",username="+username);
return "success";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="test">测试springMVC</a>
<br />
<form action="test" method="post">
<input type="submit" value="测试POST" />
</form>
</body>
</html>
带占位符的URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。
- 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
2)URL风格
示例:
/order/1 HTTP GET :得到 id = 1 的 order gerOrder?id=1
/order/1 HTTP DELETE:删除 id = 1的 order deleteOrder?id=1
/order HTTP PUT:更新order
/order HTTP POST:新增 order
扩展: 开放平台 支付功能: 你应该发送什么URL ,需要传递什么参数, 需要有哪些验证 ,响应哪些数据.
3)HiddenHttpMethodFilter
浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不
支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使
得支持 GET、POST、PUT 与 DELETE 请求。
也就是说form表单请求只支持post和get方式的请求,对于put和delete方式的请求需要依赖于HiddenHttpMethodFilter,可以在web.xml中进行配置
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>SpringMVC02</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置了之后就可以执行PUT和Delete请求了
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function test() {
$.ajax({
url:"testAjax_DELETE",
type:"DELETE",
data:{id:1001},
dataType:"json",
success:function (obj){
alert(obj);
}
});
}
</script>
</head>
<body>
<a href="testREST/1001">测试GET</a>
<br />
<form action="testREST" method="POST">
<input type="submit" value="测试POST" />
</form>
<br />
<form action="testREST" method="POST">
<input type="hidden" name="_method" value="PUT" />
<input type="submit" value="测试PUT" />
</form>
<br />
<form action="testREST/1001" method="POST">
<input type="hidden" name="_method" value="DELETE" />
<input type="submit" value="测试DELETE" />
</form>
<input type="button" value="测试AJAX" onclick="test()" />
</body>
</html>
在HiddenHttpMethodFilter中到底做了什么呢?下面是源码分析:
- 为什么请求隐含参数名称必须叫做”_method”
2.hiddenHttpMethodFilter 的处理过程
3.
* 2.如何发送PUT请求或DELETE请求?
* ①.配置HiddenHttpMethodFilter
* ②.需要发送POST请求
* ③.需要在发送POST请求时携带一个 name="_method"的隐含域,值为PUT或DELETE
HiddenHttpMethodFilter的父类是OncePerRequestFilter,它继承了父类的doFilterInternal方法,工作原理是将jsp页面的form表单的method属性值在doFilterInternal方法中转化为标准的Http方法,即GET,、POST、 HEAD、OPTIONS、PUT、DELETE、TRACE,然后到Controller中找到对应的方法。例如,在使用注解时我们可能会在Controller中用于@RequestMapping(value = "list", method = RequestMethod.PUT),所以如果你的表单中使用的是<form method="put">,那么这个表单会被提交到标了Method="PUT"的方法中。
modelAndView源码解读
ivate Object view; 用于描述视图信息
private ModelMap model; 用于描述模型数据(响应数据)
public void setViewName(String viewName) 设置视图名字
public ModelAndView addObject(String attributeName, Object attributeValue) 设置模型数据
protected Map<String, Object> getModelInternal() 返回模型数据
public ModelMap getModelMap() 返回模型数据
public Map<String, Object> getModel() 返回模型数据
2. ModelAndView 处理模型数据源码调试:
[1]. 在 DispatcherServlet中的 945 行, 开始调用请求处理器中的请求处理方法
[2]. 执行请求处理器中的请求处理方法 ,方法执行完成后,返回一个ModelAndView对象, 最终将ModelAndView对象返回到
DispatcherServlet的945行.
[3]. 在DispatcherServlet的959行, 准备开始处理ModelAndView.
[4]. 在DispatcherServlet 的 1012行,准备处理ModelAndView中的视图信息还有模型数据.
[5]. 在DispatcherServlet的1204行,根据ModelAndView中的视图信息,通过视图解析器 解析得到一个View视图对象.
[6]. 在DispatcherServlet的1225行, 视图对象开始处理模型数据
解释: render()方法是View接口中定义的。 在 AbstractView类中进行了实现. 所有具体的视图类都使用AbstractView中实现的render方法.
[7]. 在AbstractView中的266行,整合输出模型数据.
解释: renderMergedOutputModel方法是 AbstractView中定义的抽象方法, 在每个具体的视图实现类中进行实现.
[8]. 在InternalResourceView中的180行, 将模型数据设置到Request域对象中.
[9].在InternalResourceView中的189行, 获取转发器
[10].在InternalResourceView中的209行, 开始转发