1. REST
概述
- REST:即 Representational State Transfer。(资源)表现层状态转化。是目前流行的一种互联网软件架构。系统希望以非常简洁的URL地址来发请求,怎样表示对一个资源的增删改查用请求方式来区分
- 资源(Resources):网络上的一个实体可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
- 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
- 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
Rest推荐URL地址命名方式:/资源名/资源标识符
,并通过HTTP 协议四种不同请求方式区分对资源的操作
实现
HiddenHttpMethodFilter
:浏览器 form 表单只支持 GET
与 POST
请求,而DELETE
、PUT
等 method 并不支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT
与 DELETE
请求。
<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>
<a href="book/1">查询图书</a><br/>
<!-- 发送POST请求 -->
<form action="book" method="post">
<input type="submit" value="添加1号图书"/>
</form><br/>
<!-- 发送DELETE请求 -->
<form action="book/1" method="post">
<input name="_method" value="delete"/>
<input type="submit" value="删除1号图书"/>
</form><br/>
<!-- 发送PUT请求 -->
<form action="book/1" method="post">
<input name="_method" value="put"/>
<input type="submit" value="更新1号图书"/>
</form><br/>
@Controller
public class BookController extends HttpServlet{
@RequestMapping(value="/book/{bid}", method=RequestMethod.GET)
public String getBook(@PathVariable("bid")Integer bid){
System.out.println("查询到了"+bid+"号图书");
return "success";
}
@RequestMapping(value="/book/{bid}",method=RequestMethod.DELETE)
public String deleteBook(@PathVariable("bid")Integer bid){
System.out.println("删除了"+bid+"号图书");
return "success";
}
@RequestMapping(value="/book",method=RequestMethod.POST)
public String addBook(){
System.out.println("添加了新的图书");
return "success";
}
@RequestMapping(value="/book/{bid}", method=RequestMethod.PUT)
public String updateBook(@PathVariable("bid")Integer bid){
System.out.println("更新了"+bid+"号图书");
return "success";
}
}
HiddenHttpMethodFilter
源码分析:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//获取表单参数_method的值(delete\put)
String paramValue = request.getParameter(this.methodParam);
//判断是否表单是一个post而且_method有值,若不是则放行
if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
//转为大写的PUT、DELETE
String method = paramValue.toUpperCase(Locale.ENGLISH);
//重写了新 new 的对象的request.getMethod()方法
HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
//wrapper.getMethod()===PUT;
filterChain.doFilter(wrapper, response);
}
else {
//直接放行
filterChain.doFilter(request, response);
}
}
2. 映射请求参数
① 默认方式获取请求参数——直接在方法入参上写一个和请求参数名相同的变量。这个变量就来接收请求参数的值:
<a href="handle01?username=tomcat">handle01</a>
@RequestMapping(value="handle01")
public String handle02(String username){
System.out.println("这个变量的值"+username);
return "success";
}
②@RequestParam
获取请求参数;一旦标注了,则默认参数必须带;
@RequestMapping(value="handle01")
public String handle02(@RequestParam("username")String username){
System.out.println("这个变量的值"+username);
return "success";
}
相当于javaweb的:
username = request.getParameter("user")
@RequestParam
有三个参数:
-
value
:指定要获取的参数的key -
required
:这个参数是否必须的,默认true
-
defaultValue
:默认值。没带默认是null
;
③@RequestHeader
:获取请求头中某个key的值
springmv获取方式:
@RequestMapping(value="handle01")
public String handle02(@RequestHeader(value="User-Agent")String userAgent){
System.out.println("请求头中浏览器的信息:"+userAgent);
return "success";
}
相当于javaweb的:
request.getHeader("User-Agent");
@RequestHeader
有三个参数:
-
value
:指定要获取的参数的key -
required
:这个参数是否必须的,默认true
-
defaultValue
:默认值。没带默认是null
;
④@CookieValue
:获取某个cookie的值
@RequestMapping(value="handle01")
public String handle02(@CookieValue("JSESSIONID")String jid){
System.out.println("cookie:"+jid);
return "success";
}
以前的操作获取某个cookie;
//获取到request中所有的Cookie
Cookie[] cookies = request.getCookies();
for(Cookie c:cookies){
if(c.getName().equals("JSESSIONID")){ String cv = c.getValue(); }
}
@CookieValue
有三个参数:
-
value
:指定要获取的参数的key -
required
:这个参数是否必须的,默认true
-
defaultValue
:默认值。没带默认是null
;
3. 使用 POJO 对象绑定请求参数值
如果我们的请求参数是一个POJO,SpringMVC会自动的为这个POJO赋值
- 将POJO中的每一个属性,从request参数中尝试获取出来,并封装即可
- 还可以级联(属性的属性)封装
- 请求参数的参数名和对象中的属性名一一对应就行
<form action="book" method="post">
书名:<input type="text" name="bookName"/><br/>
作者:<input type="text" name="author"/><br/>
价格:<input type="text" name="price"/><br/>
库存:<input type="text" name="stock"/><br/>
销量:<input type="text" name="sales"/><br/>
<hr/>
省:<input type="text" name="address.province"/>
市:<input type="text" name="address.city"/>
街道:<input type="text" name="address.street"/><br/>
<input type="submit"/>
</form>
public class Book {
private String bookName;
private String author;
private Double price;
private Integer stock;
private Integer sales;
private Address address;
//一定有无参构造器
public String getBookName() {
return bookName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", author=" + author + ", price="
+ price + ", stock=" + stock + ", sales=" + sales
+ ", address=" + address + "]";
}
}
提交的数据可能有乱码:
- 请求乱码:
GET请求——改server.xml;在8080端口处URIEncoding="UTF-8"
POST请求——在第一次获取请求参数之前设置request.setCharacterEncoding("UTF-8");
;SpringMVC有一个filter
<!-- 配置字符编码的Filter;须注意:字符编码Filter一般都在其他Filter之前;因为Filter按照配置顺序进行拦截 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- encoding:指定解决POST请求乱码 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- forceEncoding:顺手解决响应乱码;
response.setCharacterEncoding(this.encoding); -->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 响应乱码:
response.setContentType(“text/html;charset=utf-8”)
总结:使用SpringMVC前端控制器写完就直接写字符编码过滤器;Tomcat安装后,就在server.xml的8080处添加URIEncoding="UTF-8"
4. 使用 Servlet API 作为入参
MVC 的 Handler
方法可以接受哪些 ServletAPI 类型的参数:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- Locale:国际化有关的区域信息对象
- InputStream:
ServletInputStream inputStream = request.getInputStream();
(字节流) - OutputStream:
ServletOutputStream outputStream = response.getOutputStream();
(字节流) - Reader:
BufferedReader reader = request.getReader();
(字符流) - Writer:
PrintWriter writer = response.getWriter();
(字符流)
@RequestMapping("/handle03")
public String handle03(HttpSession session,
HttpServletRequest request,HttpServletResponse response) throws IOException {
request.setAttribute("reqParam", "我是请求域中的");
session.setAttribute("sessionParam", "额我是Session域中的");
return "success";
}
5. 处理数据模型
概述
Spring MVC 提供了以下几种途径输出模型数据:
-
ModelAndView
:处理方法返回值类型为ModelAndView
时, 方法体即可通过该对象添加模型数据 -
Map
、Model
(一个接口)及ModelMap
:入参为org.springframework.ui.Model
、org.springframework.ui.ModelMap
或java.uti.Map
时,处理方法返回时,Map 中的数据会自动添加到模型中 -
@SessionAttributes
:将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性 -
@ModelAttribute
:方法入参标注该注解后,入参的对象就会放到数据模型中
具体
①SpringMVC 可以在方法处传入Map
、Model
或ModelMap
。在这些参数中保存(put()方法
)的所有数据都会放在请求域Request
中。最终在页面获取。
Map
,Model
,ModelMap
最终都是BindingAwareModelMap
在工作,即给BindingAwareModelMap
保存的信息都被放在请求域中;
@RequestMapping(value="/handle01")
public String handle01(Map<String,Object> map){
map.put("key", "data");
return "success1";
}
@RequestMapping(value="/handle02")
public String handle02(Model model){
model.addAttribute("key", "你好坏!");
return "success1";
}
@RequestMapping(value="/handle03")
public String handle03(ModelMap modelMap){
modelMap.addAttribute("key", "你好!");
return "success1";
}
②方法的返回值可为ModelAndView
类型,它既包含视图信息(页面地址)也包含模型数据(给页面带的数据),且数据放在请求域Request
中;
/**
* 返回值是ModelAndView;可以为页面携带数据
*/
@RequestMapping("/handle04")
public ModelAndView handle04(){
//之前的返回值我们就叫视图名;视图名视图解析器是会帮我们最终拼串得到页面的真实地址;
//ModelAndView mv = new ModelAndView("success");
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("msg", "你好哦!");
return mv;
}
③SpringMVC提供了可临时在Session
域中保存数据的方式——使用注解@SessionAttributes
(只能标在类上),但不推荐
例如:
@SessionAttributes(value={"msg1","msg2"},types={String.class}):
value
属性表示给BindingAwareModelMap
或ModelAndView
中保存的数据,同时给session中放一份;value
指定保存数据时要给session中放的数据的key
;types
属性只要保存的是这种类型的数据,给Session中也放一份
6. @RequestBody
-
@RequestBody
主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据); - GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。
- 在后端的同一个接收方法里,
@RequestBody
与@RequestParam()
可以同时使用,@RequestBody
最多只能有一个,而@RequestParam()
可以有多个。