什么是REST?
REST:即 Representational State Transfer。(资源)表现层状态转化。
是目前 最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用
-
资源(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 FUL?
一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
-
需求示例: /某路径/1 HTTP GET :得到 id = 1 的 user /某路径/1 HTTP DELETE:删除 id = 1的 user /某路径 HTTP PUT :更新查询id = 1的 user,进行PUT请求更新 /某路径 HTTP POST :新增 user
http://localhost:8080/pro/user/1 GET
http://localhost:8080/pro/user/1 DELETE
http://localhost:8080/pro/user/1 PUT
http://localhost:8080/pro/user/ POST
@PathVariable
注解:映射 URL 绑定的占位符
带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的 意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控 制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过 @PathVariable(“xxx”) 绑定到操作方法的入参中。
/** * REST风格 */ @RequestMapping("/rest/{name}/{age}") @ResponseBody public String rest(@PathVariable("name") String name, @PathVariable Integer age, Model model){ return "hello:"+name+",age="+age; }
@PathVariable:不能省略
@PathVariable("name"):中“name"如果有{name}名称相同,可以省略"name"
优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀,需将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获 WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他 们当成一个普通请求处理,因找不到对应处理器将导致错误
SpringMVC访问静态文件
<!--静态资源--> <mvc:resources mapping="/statics/**" location="/statics/"/>
mapping:将静态资源映射到指定的路径下 location:本地静态资源文件所在的目录
数据格式化
-
实现方式
-
接口方式:org.springframework.format.Formatter
-
注解方式:org.springframework.format.AnnotationFormatterFactory
-
-
内置API
-
@DateTimeFormat
-
@NumberFormat
-
-
全局注册
-
Java configuration
-
XML-based configuration
-
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="registerDefaultFormatters" value="false" />
<property name="formatters">
<set>
<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
<bean class="org.springframework.format.datetime.DateFormatter" p:pattern="yyyy-MM-dd" />
</set>
</property>
<property name="formatterRegistrars">
<set>
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
<property name="dateFormatter">
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
<property name="pattern" value="yyyyMMdd"/>
</bean>
</property>
</bean>
</set>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>
日期默认情况下转换失败
HTTP Status 400 – Bad Request
异常处理器
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
异常处理思路
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
自定义异常类
为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
public class CustomException extends RuntimeException { /** serialVersionUID*/ private static final long serialVersionUID = -5212079010855161498L; public CustomException(String message){ super(message); this.message = message; } //异常信息 private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
根据项目需求 创建多种异常,但一般继承RuntimeException
自定义异常处理器
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
CustomException customException = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个未知错误异常。
customException = new CustomException("未知错误,请与系统管理 员联系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
错误页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>错误页面</title>
</head>
<body>
您的操作出现错误如下:<br/>
${message }
</body>
</html>
异常处理器配置
在springmvc.xml中添加:
<!-- 异常处理器 --> <bean id="handlerExceptionResolver" class="com.zking.ssm.exception.CustomExceptionResolver"/>
handlerExceptionResolver名称不能更名,同multipartResolver
异常测试
修改商品信息,id输入错误提示商品信息不存在。
修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常:
// 调用service查询商品信息 Items item = itemService.findItemById(id); if(item == null){ throw new CustomException("商品信息不存在!"); }
请自行实现在service、dao中跑出异常。
json数据交互
@RequestBody
作用:
@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json
@ResponseBody
作用:
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端
在WebMvcConfigurationSupport中的addDefaultHttpMessageConverters添加默认实现
HttpMessageConvert<T>负责将一个请求信息转换为一个对象(类型为T),将对象(类型为T)转换为响应信息。DispatcherServlet默认安装了AnnotationMethodHandlerAdapter做为HandlerAdapter的组件实现类,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,将请求信息转换成对象,或将对象转换成响应信息
参数解析
InvocableHandlerMethod
在HandlerAdapter执行目标方法之前,需要调用resolveArgument() 依次解析出每个入参对象,然后才会执行目标方法
HandlerMethodArgumentResolverComposite
在resolveArgument()内部,首先调用getArgumentResolver() 获得能解析当前入参的HandlerMethodArgumentResolver解析器,然后调用这个解析器的resolveArgument() 获得一个入参对象。
getArgumentResolver()
会返回什么类型的HandlerMethodArgumentResolver
,这与入参类型和入参上的注解有关,Spring MVC默认注册了以下方法参数解析器:有一些解析器是调用binder.convertIfNecessary(),使用绑定器的ConversionService内部适当的Converter进行类型转换,通常是字符串转入参对象(因为是超文本协议嘛),比如PathVariableMethodArgumentResolver、RequestParamMethodArgumentResolver等。
有一些解析器是调用readWithMessageConverters(),使用适当的HttpMessageConverter将HTTP请求转换为入参对象,比如RequestResponseBodyMethodProcessor、HttpEntityMethodProcessor等。
响应json实现:
环境准备
Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入fastjson的包,如下:
<!-- 阿里fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.4</version> </dependency>
乱码
-
方式一:
-
produces="application/json; charset=utf-8"
-
/** * 新增用户 */ @PostMapping(produces="application/json; charset=utf-8") public String add(@RequestBody SysUser sysUser){ return "新增用户"; }
-
方式二:
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
响应类
public class AjaxResult {
private boolean success = true;
private String msg="";
public boolean getSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
controller方式一
@Controller
@RequestMapping("/demo4")
public class JsonController {
/**
* 前端通过ajax请求checkUserName方法,判断此用户名是否存在
* @ResponseBody:将当前方法,以Ajax方式响应客户端
*/
@ResponseBody
@RequestMapping("/checkUserName")
public AjaxResult checkUserName(){
AjaxResult ajaxResult = new AjaxResult();
ajaxResult.setMsg("");
return ajaxResult;
}
}
@ResponseBody将当前方法,以Ajax方式响应客户端
controller方式二
@RestController
@RequestMapping("/demo4")
public class JsonController2 {
/**
* 前端通过ajax请求checkUserName方法,判断此用户名是否存在
*/
@RequestMapping("/checkUserName2")
public AjaxResult checkUserName(){
AjaxResult ajaxResult = new AjaxResult();
ajaxResult.setMsg("");
return ajaxResult;
}
}@RestController
@RequestMapping("/demo4")
public class JsonController2 {
/**
* 前端通过ajax请求checkUserName方法,判断此用户名是否存在
*/
@RequestMapping("/checkUserName2")
public AjaxResult checkUserName(){
AjaxResult ajaxResult = new AjaxResult();
ajaxResult.setMsg("");
return ajaxResult;
}
}
@RestController将Controller中的所有方法以Ajax方法响应
ajax请求
$.ajax({
type:"GET",//请求类型
url:path+"/jsp/user.do",//请求的url
data:{method:"ucexist",userCode:userCode.val()},//请求参数
dataType:"json",//ajax接口(请求url)返回的数据类型
success:function(data){//data:返回数据(json对象)
if(data.userCode == "exist"){//账号已存在,错误提示
validateTip(userCode.next(),{"color":"red"},imgNo+ " 该用户账号已存在",false);
}else{//账号可用,正确提示
validateTip(userCode.next(),{"color":"green"},imgYes+" 该账号可以使用",true);
}
},
error:function(data){//当访问时候,404,500 等非200的错误状态码
validateTip(userCode.next(),{"color":"red"},imgNo+" 您访问的页面不存在",false);
}
});
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="ajaxAdd" id="userForm">
<p>
登录名:<input type="text" name="userCode" id="userCode"/>
</p>
<p>
密码:<input type="password" name="userPassword" id="userPassword">
</p>
<p><input type="button" value="注册" id="add"/> </p>
</form>
<script src="../statics/js/jquery-1.8.3.min.js"></script>
<script>
$(function() {
/*$("#add").bind("click",function(){
var userCode = $("#userCode").val();
var userPassword = $("#userPassword").val();
$.ajax({
type: "GET",//请求类型
url: "../demo4/ajaxAdd",//请求的url
data: {userCode: userCode,userPassword:userPassword},//请求参数
dataType: "json",//ajax接口(请求url)返回的数据类型
success: function (data) {//data:返回数据(json对象)
console.log(data.success + ":" + data.msg);
},
error: function (data) {//当访问时候,404,500 等非200的错误状态码
console.log(data)
}
})
});*/
/**
contentType:"application/json"
*/
/*$("#add").bind("click",function(){
var userCode = $("#userCode").val();
var userPassword = $("#userPassword").val();
$.ajax({
type: "POST",//请求类型
url: "../demo4/ajaxAdd",//请求的url
contentType:"application/json",//请求内容类型
data: {userCode: userCode,userPassword:userPassword},//请求参数
dataType: "json",//ajax接口(请求url)返回的数据类型
success: function (data) {//data:返回数据(json对象)
console.log(data.success + ":" + data.msg);
},
error: function (data) {//当访问时候,404,500 等非200的错误状态码
console.log(data)
}
})
});*/
/**
* Content-Type: application/x-www-form-urlencoded; charset=UTF-8
*/
/* $("#add").bind("click",function(){
$.ajax({
type: "POST",//请求类型
url: "../demo4/ajaxAdd",//请求的url
data: $("#userForm").serializeArray(),
dataType: "json",//ajax接口(请求url)返回的数据类型
success: function (data) {//data:返回数据(json对象)
console.log(data.success + ":" + data.msg);
},
error: function (data) {//当访问时候,404,500 等非200的错误状态码
console.log(data)
}
})
});*/
/**
* 请求数据为符合JSON格式的字符串
*/
$("#add").bind("click",function(){
var userCode = $("#userCode").val();
var userPassword = $("#userPassword").val();
var user={'name':userCode,'passwd':userPassword} //json对象
$.ajax({
type: "POST",//请求类型
url: "../demo4/ajaxAdd2",//请求的url
contentType: "application/json",
data: JSON.stringify(user), //将json对象user转变成字符串
dataType: "json",//ajax接口(请求url)返回的数据类型
success: function (data) {//data:返回数据(json对象)
console.log(data.success + ":" + data.msg);
},
error: function (data) {//当访问时候,404,500 等非200的错误状态码
console.log(data)
}
})
});
})
</script>
</body>
</html>