Java——Restful风格
REST与RESTful:
REST:表现层状态转移,资源在网络中以某种形式进行状态转移。
RESTful是基于REST理念的一套开发风格,是具体的开发规则。
服务器端只返回数据,以json或者xml的格式。
RESTful开发规范:
• 使用URL作为用户交互入口
• 明确的语义规范(GET|POST|PUT|DELETE)
• 只返回数据(JSON|XML),不包含任何展现
RESTful命名要求:
1.第一个RESTful应用
@Controller @RequestMapping("/restful") //URL中所有的都是名词 public class RestfulController { @GetMapping(value = "/request",produces = "application/json;charset=utf-8") @ResponseBody public String doGetRequest(){ return "{\"message\":\"测试\"}"; //使用\原义输出 } }
2.实现RESTful实验室
一般PC和移动端都可以调用API接口,下面模拟PC端调用,使用Ajax:
通过ajax发送页面请求:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>RESTful</title> <script src="jquery-3.4.1.min.js"></script> <script> $(function () { $("#btnGet").click(function () { $.ajax({ url : "/restful/request", type : "get", dataType : "json", success : function (json) { $("#message").text(json.message) } }) }) }) </script> </head> <body> <input type="button" id="btnGet" value="发送Get请求"> <h2 id="message"></h2> </body> </html>
因为定义了webapp为静态文件的根目录,所以client.html可以直接访问。
竟然产生了乱码,查看请求头,发现采用不正确的字符集。
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>test/html;charset=utf-8</value> <!--通知浏览器以这种格式加载数据--> <value>application/json;charset=utf-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
添加以上配置就可以了。(只能解决ajax乱码问题)
3.RestController注解与路径变量
(1)RestController
@Controller @RequestMapping("/restful") //URL中所有的都是名词 public class RestfulController { @GetMapping(value = "/request",produces = "application/json;charset=utf-8") @ResponseBody public String doGetRequest(){ return "{\"message\":\"测试\"}"; //使用\原义输出 } }
如果我们希望返回纯文本数据,我们必须要使用@ResponseBody这个注解。
如果我们使用@RestController这个注解,那么类下面所有方法都是返回纯文本数据。
@RestController @RequestMapping("/restful") //URL中所有的都是名词 public class RestfulController { @GetMapping(value = "/request",produces = "application/json;charset=utf-8") public String doGetRequest(){ return "{\"message\":\"测试\"}"; //使用\原义输出 } }
@RestController可以帮我们简化开发。
(2)路径变量
/request/1 对于放在URL中的变量我们可以称之为路径变量。那么如何取值了?
@RestController @RequestMapping("/restful") //URL中所有的都是名词 public class RestfulController { @GetMapping(value = "/request/{rid}",produces = "application/json;charset=utf-8") public String doGetRequest(@PathVariable("rid") Integer requestId){ System.out.println(requestId); return "{\"message\":\"测试\"}"; //使用\原义输出 } }
使用@PathVariable路径变量注解进行接收,而后赋值给方法参数。
4.JSON序列化
(1)引入步骤
导入依赖包:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <!--一定要使用2.9.9之后的版本,否则会有安全问题--> <version>2.9.9</version> </dependency> <!--jackson与目标对象交互的根源--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.9</version> </dependency>
spring非常智能,只要检查有jackson-core和jackson-databind这两个依赖包。
就会自动启用jackson为我们提供json序列化服务。
创建实体类:
public class Person { private String name; private String address; }
编写控制器:
@GetMapping("/person/{pid}") public Person findByPersonId(@PathVariable("pid") Integer personId){ Person person = new Person(); if (personId == 1){ person.setName("科比"); person.setAddress("湖北罗田"); } else if (personId == 2){ person.setName("星爷"); person.setAddress("湖北安陆"); } else { person.setName("无名氏"); } return person; }
如果我们返回一个实体对象,并且配置了@RestController或者@ResponseBody,那么jackson就会自动提供序列化服务。
访问:
(2)返回多个对象
如果一次返回多个对象,我们可以List集合:
@GetMapping("/persons") public List<Person> findPersons(){ List list = new ArrayList(); Person p1 = new Person(); p1.setName("科比"); p1.setAddress("湖北罗田"); Person p2 = new Person(); p2.setName("科比"); p2.setAddress("湖北罗田"); list.add(p1); list.add(p2); return list; }
在前端,我们会收到如下数据:
在页面中,我们可以通过如下方式进行提取:
$(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.address + "</h2>") } } }) }) })
(3)时间处理
需要注意的是,jackson对时间处理并不友好:
添加事件属性:
private Date birthday;
如果不做处理,就是直接返回事件戳的形式。
我们只需要添加对应时间注解:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
就可以正常输出了:
还有需要注意的是默认使用格林时间,需要指定时区:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date birthday;
5.浏览器的同源策略
同源策略:阻止从一个域加载的脚本去获取另一个域上的资源。
两个不同域名的网站不能通过Ajax访问,这是出于安全的因素考虑。
比如下面两个地址,虽然本质是一个页面,但是却属于不同源.
只要协议、域名、端口有任何不同,都被当做是不同的域。
浏览器Console看到Access-Control-Allow-Origin就代表跨域了。
HTML中允许跨域的标签:
<img> 显式远程图片
<script> 加载远程JS
<link> 加载远程CSS
6.SpringMVC解决跨域
CORS是一种机制,使用额外的HTTP头通知浏览器访问其他域。
URL响应头中包含Access-Control-*指明请求允许跨域。
(1)@CrossOrigin - Controller跨域注解
@RestController @RequestMapping("/restful") @CrossOrigin(origins = {"*"}) public class RestfulController { @GetMapping(value = "/request/{rid}",produces = "application/json;charset=utf-8") public String doGetRequest(@PathVariable("rid") Integer requestId){ System.out.println(requestId); return "{\"message\":\"测试\"}"; } }
(2)<mvc:cors> Spring MVC全局跨域配置
<mvc:cors> <!--path哪一个路径允许跨域访问--> <!--allowed-origins允许谁进行跨域访问--> <!--max-age设置缓存时间--> <mvc:mapping path="*" allowed-origins="*"/> </mvc:cors>