RESTful风格的应用
文章目录
传统Web应用的问题
只能通过HTML进行通信
REST与RESTful
REST - 表现层状态转换,资源在网络中以某种表现形式进行状态转移
RESTful是基于REST理念的一套开发风格,是具体的开发规则
RESTful传输数据
根据发起的请求返回固定格式的数据
RESTful开发规范
- 使用URL作为用户交互入口
- 明确的语意规范(GET|POST|PUT|DELETE)
- 只返回数据(JSON|XML),不包含任何展现
RESTful命名要求
开发RESTful Web应用
创建一个maven工程,引入并配置web模块
https://blog.csdn.net/a2272062968/article/details/120404192
pom.xml引入spring-webmvc
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
</dependencies>
web.xml配置DispatchServlet和字符编码转换过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--DispatchServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<!--
DispatcherServlet是Spring MVC最核心的对象
DispatcherServlet用于拦截Http请求,
并根据请求的URL调用与之对应的controller方法,来完成Http请求的处理
-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--
在web应用启动时自动创建spring Ioc容器并且初始化DispatcherServlet
如果忘记写也可以运行,但是在第一次访问的时候创建,为了提高效率建议在web程序启动但时候创建
-->
<load-on-startup>0</load-on-startup>
</servlet>
<!--映射-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--"/"代表拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 过滤器-CharacterEncodingFilter对post所有请求转为UTF-8-->
<filter>
<filter-name>characterFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
applicationContext.xml创建并配置注解开发
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--
context:component-scan 标签作用
在Spring IOC初始化过程中,自动创建并且管理springmvc及子包中
拥有以下注解对象
@Repository 通常使用这个的类都是与数据发生直接交互的类
@Service 业务逻辑类,通常放在xxxService
@Controller 控制器类 Spring控制器
@Component 不好区分类型使用这个
-->
<context:component-scan base-package="restful"/>
<!--启用Spring MVC注解开发模式-->
<mvc:annotation-driven>
<mvc:message-converters> <!--消息转换器,解决html中对中文乱码问题-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--response.setContentType=("text/html;charset=utf-8")-->
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--将图片/js/css等静态资源排除在外,可提高执行效率-->
<mvc:default-servlet-handler/>
</beans>
restful.controller.RestfulController创建控制器类
package restful.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
@ResponseBody
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
}
执行tomcat,记得添加部署引用文件,访问localhost/restful/request即可现实上面写的json格式数据
利用这个原理,可以使用ajax开发页面的交互
实现RESTful实验室
RESTful基础实现
webapp下引入jquery
创建client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery-3.6.0.min.js"></script>
<script>
$(function (){
$("#btnGet").click(function (){
$.ajax({
url:"/restful/request",
type:"get",
dataType:"json",
success:function (json){
$("#message").text(json.message);
console.log(json);
}
})
})
})
</script>
</head>
<body>
<input type="button" id="btnGet" value="发送Get请求">
<h1 id="message"></h1>
</body>
</html>
访问http://localhost:8080/client.html点击按钮即可在h1中现实json的信息
中文乱码问题,可以在浏览器网络里看到相应头编码(我测试时没出现乱码)
解决方案:applicationContext.xml中消息转换器部分list里增加:
<value>application/json;charset=utf-8</value>
其他功能实现
上面的程序简单修改增加一下,就可以实现Get,Post,Put和Delete的功能
restful.controller.RestfulController
package restful.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
@ResponseBody
public String doGetRequest(){
return "{\"message\":\"返回查询结果\"}";
}
@PostMapping("/request") //因为是post请求,所以名字和上面一样不冲突
@ResponseBody
public String doPostRequest(){
return "{\"message\":\"数据新建成功\"}";
}
@PutMapping("/request")
@ResponseBody
public String doPutRequest(){
return "{\"message\":\"数据更新成功\"}";
}
@DeleteMapping("/request")
@ResponseBody
public String doDeleteRequest(){
return "{\"message\":\"数据删除成功\"}";
}
}
client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery-3.6.0.min.js"></script>
<script>
$(function (){
$("#btnGet").click(function (){
$.ajax({
url:"/restful/request",
type:"get",
dataType:"json",
success:function (json){
$("#message").text(json.message);
}
})
})
})
$(function (){
$("#btnPost").click(function (){
$.ajax({
url:"/restful/request",
type:"post",
dataType:"json",
success:function (json){
$("#message").text(json.message);
}
})
})
})
$(function (){
$("#btnPut").click(function (){
$.ajax({
url:"/restful/request",
type:"put",
dataType:"json",
success:function (json){
$("#message").text(json.message);
}
})
})
})
$(function (){
$("#btnDelete").click(function (){
$.ajax({
url:"/restful/request",
type:"delete",
dataType:"json",
success:function (json){
$("#message").text(json.message);
}
})
})
})
</script>
</head>
<body>
<input type="button" id="btnGet" value="发送Get请求">
<input type="button" id="btnPost" value="发送Post请求">
<input type="button" id="btnPut" value="发送Put请求">
<input type="button" id="btnDelete" value="发送Delete请求">
<h1 id="message"></h1>
</body>
</html>
RestController注解与路径变量
之前的程序,每一个方法上都要声明@ResponseBody注解来说明是输出相应而不是页面跳转
可以在类的最上方将@Controller改为@RestController来简化开发
路径变量是存放在URL中可变的数值
RestfulController
// POST /restful/request/100
@PostMapping("/request/{rid}")
public String doPostRequest(@PathVariable("rid") Integer requestId){
return "{\"message\":\"数据新建成功\",\"id\":"+requestId+"}";
}
client.html在post请求url中增加数值100
$(function (){
$("#btnPost").click(function (){
$.ajax({
url:"/restful/request/100",
type:"post",
dataType:"json",
success:function (json){
$("#message").text(json.message+":"+json.id);
}
})
})
})
简单请求与非简单请求
- 简单请求是指标准结构的HTTP请求,对应GET/POST请求
- 非简单请求是复杂要求的HTTP请求,指PUT/DELETE,扩展标准请求
- 两者最大区别是非简单请求发送前需要发送预检请求
非简单请求
为之前的代码post,put增加请求数据data
$(function (){
$("#btnPost").click(function (){
$.ajax({
url:"/restful/request/100",
type:"post",
data:"name=lily&age=23",
dataType:"json",
success:function (json){
$("#message").text(json.message+":"+json.id);
}
})
})
})
$(function (){
$("#btnPut").click(function (){
$.ajax({
url:"/restful/request",
type:"put",
data:"name=lily&age=23",
dataType:"json",
success:function (json){
$("#message").text(json.message);
}
})
})
})
新建Person实体类
restful/entity/Person
package restful.entity;
public class Person {
private String name;
private Integer age;
//get/set方法
}
RestfulController参数增加person
// POST /restful/request/100
@PostMapping("/request/{rid}") //因为是post请求,所以名字和上面一样不冲突
public String doPostRequest(@PathVariable("rid") Integer requestId, Person person){
System.out.println(person.getName()+person.getAge());
return "{\"message\":\"数据新建成功\",\"id\":"+requestId+"}";
}
@PutMapping("/request")
public String doPutRequest(Person person){
System.out.println(person.getName()+person.getAge());
return "{\"message\":\"数据更新成功\"}";
}
运行后发现,只有post请求输出了数据,而put请求则是null
需要在web.xml中定义过滤器
<!--自定义formContentFilter过滤器,利用FormContentFilter对put和delete请求进行过滤,过滤后即可获取到参数-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
再次运行即可成功接收到请求参数数据
JSON序列化
Jackson
jackson2.9版本之前和Mysql使用会有严重的系统安全漏洞可能被黑客利用,一定要选择2.9之后的
<!-- json序列化核心包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<!-- json与目标对象交互对包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- json注解开发-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
返回单个对象
RestfulController增加测试方法
@GetMapping("/person")
//这里返回对是Person类型而不是String了,因为Jackson会自动帮我们拼接json格式字符串返回
public Person findByPersonId(Integer id){
Person p = new Person();
if (id == 1) {
p.setName("lili");
p.setAge(23);
} else if (id == 2) {
p.setName("smith");
p.setAge(22);
}
return p;
}
运行tomcat,注意添加刚刚引用的jar包部署
浏览器访问http://localhost:8080/restful/person?id=1就能看到数据
返回多个对象
RestfulController
@GetMapping("/person")
public List<Person> findPersons(){
List list = new ArrayList();
Person p1 = new Person();
p1.setName("lily");
p1.setAge(23);
Person p2 = new Person();
p2.setName("smith");
p2.setAge(22);
list.add(p1);
list.add(p2);
return list;
}
利用ajax在html页面中交互数据
client.html
<script>
$(function (){
$("#btnPersons").click(function (){
$.ajax({
url: "restful/persons",
type: "get",
dataType: "json",
success: function (json) {
console.info(json);
for (var i = 0; i < json.length; i++) {
var p = json[i];
$("#divPersons").append("<h2>" + p.name + "-" + p.age + "</h2>");
}
}
})
})
})
</script>
......
<input type="button" id="btnPersons" value="查询所有人员">
<div id="divPersons"></div>
点击查询所有人员按钮,显示出传递的信息
@JsonFormat处理日期类型
为Person类增加一个Date birthday属性
RestfulController直接传递当前时间p1.setBirthday(new Date());
client.html页面增加p.birthday显示
此时访问页面点击查询按钮,日期部分显示的是一串数字"1636352881734",1970年到该日期的毫秒数
解决方案
在实体类日期属性上面增加输出格式注解
//timezone跟默认对伦敦平移8个时区
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birthday;
@JsonFormat还可以对数字,货币等按格式化进行输出
浏览器的跨域访问
浏览器对同源策略
- 同源策略阻止一个域加载的脚本去获取另一个域上的资源
- 只要协议,域名,端口有任何一个不同,都被当作是不同的域
- 浏览器Console看到Access-Control-Allow-Origin就代表跨域了
比如页面A使用ajax请求访问页面B就会被浏览器的同源策略阻止,以此来保护网站的安全性
同源策略示例
HTML中允许跨域的标签
<img> - 显示远程图片
<script> - 加载远程JS
<link> - 加载远程CSS
Spring MVC跨域访问
CORS跨域资源访问
CORS是一种机制,使用额外的HTTP头通知浏览器可以访问其他域
URL相应头包含Access- Control-*指明请求允许跨域
Spring MVC解决跨域访问
@CrossOrigin - Controller跨域注解
<mvc:cors> - Spring MVC全局跨域配置
注意都是在被访问的服务器部分部署配置,只要是远程访问当前服务,都会被管理,可以指定允许访问的远程链接
方案一:注解指定控制器类配置
在控制器类上增加@CrossOrigin注解;允许来自本届8056端口和百度的访问
@CrossOrigin(origins={"http://localhost:8056","http://www.baidu.com"})
所有域名都可以访问:@CrossOrigin(origins="*")
实际开发不推荐使用!!!
可以设置预检请求的相应时间为3600秒,将预检请求的结果缓存,回到上面的非简单请求,设置预检请求可以让服务器在该时间段内对非简单请求不用发送预检请求,直接发送实际请求,不用发送2次请求了
@CrossOrigin(origins="*",maxAge=3600)
方案二:全局配置
applicationContext.xml
<mvc:cors>
<mvc:mapping path="/restful/**"
allowed-origins="http://localhost:8056,http://www.baidu.com"
max-age="3600"/>
</mvc:cors>