您的位置:首页 > 编程语言 > Java开发

笔记67 Spring Boot快速入门(七)

2018-07-15 18:24 573 查看

笔记67 Spring Boot快速入门(七)

SpringBoot+RESTful+JSON

一、RESTful架构

  REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。" 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

  REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。

1.统一资源接口

  RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET,PUT和POST,并遵循这些方法的语义。

  如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性,例如GET和HEAD请求都是安全的, 无论请求多少次,都不会改变服务器状态。而GET、HEAD、PUT和DELETE请求都是幂等的,无论对资源操作多少次, 结果总是一样的,后面的请求并不会产生比第一次更多的影响(幂等)。

  下面列出了GET,DELETE,PUT和POST的典型用法:

GET DELETE PUT POST
  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)
  • 200(OK) - 表示已在响应中发出
  • 204(无内容) - 资源有空表示
  • 301(Moved Permanently) - 资源的URI已被更新
  • 303(See Other) - 其他(如,负载均衡)
  • 304(not modified)- 资源未更改(缓存)
  • 400 (bad request)- 指代坏请求(如,参数错误)
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求
  • 不安全且不幂等
  • 使用服务端管理的(自动产生)的实例号创建资源
  • 创建子资源
  • 部分更新资源
  • 如果没有被修改,则不过更新资源(乐观锁)
  • 200(OK)- 如果现有资源已被更改
  • 201(created)- 如果新资源被创建
  • 202(accepted)- 已接受处理请求但尚未完成(异步处理)
  • 301(Moved Permanently)- 资源的URI被更新
  • 303(See Other)- 其他(如,负载均衡)
  • 400(bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求
  • 不安全但幂等
  • 用客户端管理的实例号创建一个资源
  • 通过替换的方式更新资源
  • 如果未被修改,则更新资源(乐观锁)
  • 200 (OK)- 如果已存在资源被更改
  • 201 (created)- 如果新资源被创建
  • 301(Moved Permanently)- 资源的URI已更改
  • 303 (See Other)- 其他(如,负载均衡)
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求
  • 不安全但幂等
  • 删除资源
  • 200 (OK)- 资源已被删除
  • 301 (Moved Permanently)- 资源的URI已更改
  • 303 (See Other)- 其他,如负载均衡
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 409 (conflict)- 通用冲突
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求

小结:

<1>POST和PUT用于创建资源时有什么区别?

  POST和PUT在创建资源的区别在于,所创建的资源的名称(URI)是否由客户端决定。 例如为我的博文增加一个java的分类,生成的路径就是分类名/categories/java,那么就可以采用PUT方法。

<2>PATCH这种不是HTTP标准方法。

<3>统一资源接口对URI有什么指导意义?

统一资源接口要求使用标准的HTTP方法对资源进行操作,所以URI只应该来表示资源的名称,而不应该包括资源的操作。

通俗来说,URI不应该使用动作来描述。例如,下面是一些不符合统一接口要求的URI:

  • [li] GET /getUser/1
  • POST /createUser
  • PUT /updateUser/1
  • DELETE /deleteUser/1
[/li]

二、RESTful风格

  简而言之,什么是REST? 就是看Url就知道要什么,看http method就知道干什么。

  在Web开发的过程中,method常用的值是get和post。可事实上,method值还可以是put和delete等等其他值。
既然method值如此丰富,那么就可以考虑使用同一个url,但是约定不同的method来实施不同的业务,这就是Restful的基本考虑。
CRUD是最常见的操作,在使用Restful 风格之前,通常的增加做法是这样的:/addCategory?name=xxx

可是使用了Restuful风格之后,增加就变成了:/category

CRUD如下表所示,URL就都使用一样的 "/category",区别只是在于method不同,服务器根据method的不同来判断浏览器期望做的业务行为。

1.CategoryController.java

  CRUD的RequestMapping都修改为了/category,以前用的注解叫做@RequestMapper,现在分别叫做 GetMapper, PutMapper, PostMapper 和 DeleteMapper 用于表示接受对应的Method。

1 package com.example.springbootrestfuldemo.controller;
2
3
4 import com.example.springbootrestfuldemo.dao.CategoryDAO;
5 import com.example.springbootrestfuldemo.pojo.Category;
6 import com.sun.org.apache.xpath.internal.operations.Mod;
7 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.data.domain.Page;
9 import org.springframework.data.domain.PageRequest;
10 import org.springframework.data.domain.Pageable;
11 import org.springframework.data.domain.Sort;
12 import org.springframework.stereotype.Controller;
13 import org.springframework.ui.Model;
14 import org.springframework.web.bind.annotation.*;
15
16 import java.util.List;
17
18 @Controller
19 public class CategoryController {
20     @Autowired
21     CategoryDAO categoryDAO;
22
23     //restful风格
24
25     //1.返回5条记录
26     @GetMapping("/category")
27     public String listCategory(Model model,@RequestParam(value = "start", defaultValue = "0") int start, @RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
28         start = start<0?0:start;
29         Sort sort = new Sort(Sort.Direction.DESC, "id");
30         Pageable pageable = new PageRequest(start, size, sort);
31         Page<Category> page =categoryDAO.findAll(pageable);
32         model.addAttribute("page",page);
33         return "listCategories";
34     }
35     //2.保存一条记录
36     @PutMapping("/category")
37     public String addCategories(Category category) throws Exception {
38         System.out.println("保存一条记录");
39         categoryDAO.save(category);
40         return "redirect:/category";
41     }
42     //3.删除一条记录
43     @DeleteMapping("/category/{id}")
44     public String deleteCategory(Category category){
45         System.out.println("删除一条记录!");
46         categoryDAO.delete(category);
47         return "redirect:/category";
48     }
49
50     //4.更新一条记录
51     @PostMapping("/category/{id}")
52     public String updateCategory(Category category){
53         System.out.println("更新一条记录!");
54         categoryDAO.save(category);
55         return "redirect:/category";
56     }
57     //6.跳转到编辑页
58     @GetMapping("/category/{id}")
59     public String addCategory(@PathVariable("id") int id, Model model) throws Exception{
60         System.out.println("编辑视图");
61         Category category=categoryDAO.getOne(id);
62         model.addAttribute("c",category);
63         return "editCategory";
64     }
65
66 }

2.listCategories.html

1 <!DOCTYPE html>
2 <html lang="en" xmlns:th="http://www.thymeleaf.org">
3 <head>
4     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5     <title>Title</title>
6 </head>
7 <body>
8 <div class="showing">
9     <h2>springboot+jpa+thymeleaf+Restful</h2>
10     <div style="width:500px;margin:20px auto;text-align: center">
11         <table align="center" border="1" cellspacing="0">
12             <thead>
13             <tr>
14                 <th>id</th>
15                 <th>name</th>
16                 <td>编辑</td>
17                 <td>删除</td>
18             </tr>
19             </thead>
20             <tbody>
21             <tr th:each="c: ${page}">
22                 <td align="center" th:text="${c.id}"></td>
23                 <td align="center" th:text="${c.name}"></td>
24                 <td align="center" ><a th:href="@{/category/{id}(id=${c.id})}">编辑</a></td>
25                 <td>
26                     <form  th:action="@{/category/{id}(id=${c.id})}" action="/category" method="post">
27                         <input type="hidden" name="_method" value="DELETE"/>
28                         <button type="submit">删除</button>
29                     </form>
30                 </td>
31             </tr>
32             </tbody>
33         </table>
34         <br />
35         <div>
36             <a th:href="@{/category(start=0)}">[首  页]</a>
37             <a th:href="@{/category(start=${page.number -1})}">[上一页]</a>
38             <a th:if="${page.number!=page.totalPages -1}" th:href="@{/category(start=${page.number +1})}">[下一页]</a>
39             <a th:href="@{/category(start=${page.totalPages -1})}">[末  页]</a>
40         </div>
41         <form action="/category" method="post">
42             <!--修改提交方式为PUT-->
43             <input type="hidden" name="_method" value="PUT"/>
44             name:<input name="name"/><br/>
45             <button type="submit">提交</button>
46         </form>
47
48
49     </div>
50 </div>
51 </body>
52 </html>

<1>增加

A:action修改为"/category"
B:增加如下<input type="hidden" name="_method" value="PUT">,虽然这个form的method是post, 但是spingboot看到这个_method的值是put后,会把其修改为put.

1         <form action="/category" method="post">
2             <!--修改提交方式为PUT-->
3             <input type="hidden" name="_method" value="PUT"/>
4             name:<input name="name"/><br/>
5             <button type="submit">提交</button>
6         </form>

<2>删除

  原来的删除是通过超链接进行跳转,通过jQuery或者js可以将超链接的提交方式改变成成表单提交。然而我不会,所以用笨办法,<td>里面直接放一个form。

1                 <td>
2                     <form  th:action="@{/category/{id}(id=${c.id})}" action="/category" method="post">
3                         <input type="hidden" name="_method" value="DELETE"/>
4                         <button type="submit">删除</button>
5                     </form>
6                 </td>

<3>修改

注意thymeleaf构建url的方式

1 <td align="center" ><a th:href="@{/category/{id}(id=${c.id})}">编辑</a></td>

3.editCategory.html

修改action中的地址

1 <!DOCTYPE html>
2 <html lang="en" xmlns:th="http://www.thymeleaf.org">
3 <head>
4     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5     <title>Title</title>
6 </head>
7 <body>
8 <div class="showing">
9     <h2>springboot+jpa</h2>
10
11     <div style="margin:0px auto; width:500px">
12
13         <form th:action="@{/category/{id}(id=${c.id})}"  method="post">
14
15             name: <input name="name"  th:value="${c.name}" /> <br/>
16
17             <input name="id" type="hidden" th:value="${c.id}" />
18             <button type="submit">提交</button>
19
20         </form>
21     </div>
22 </div>
23 </body>
24 </html>

4.完整代码:https://github.com/lyj8330328/springboot-restful-demo

三、分别是以json方式:提交,获取单个和获取多个

提交:http://localhost:8080/submit.html

获取单个:http://localhost:8080/getOne.html

获取多个:http://localhost:8080/getMany.html

基于Restful 风格的springboot进行修改。

1.修改Category.java

①增加个toString() 方便,便于显示
②增加个注解:@JsonIgnoreProperties({ "handler","hibernateLazyInitializer" }) ,否则会出错

1 package com.example.springbootjpademo.pojo;
2
3 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4
5 import javax.persistence.*;
6
7 @Entity
8 @Table(name = "category")
9 @JsonIgnoreProperties({ "handler","hibernateLazyInitializer" })
10 public class Category {
11     @Id
12     @GeneratedValue(strategy = GenerationType.IDENTITY)
13     @Column(name = "id")
14     private int id;
15
16     @Column(name = "name")
17     private String name;
18     public int getId() {
19         return id;
20     }
21     public void setId(int id) {
22         this.id = id;
23     }
24     public String getName() {
25         return name;
26     }
27     public void setName(String name) {
28         this.name = name;
29     }
30
31     public String toString() {
32         return "Category [id=" + id + ", name=" + name + "]";
33     }
34 }

2.CategoryController.java

控制器里提供3个方法,分别用来处理json 提交,json获取单个对象,json获取多个对象。

1 package com.example.springbootjpademo.controller;
2
3 import com.example.springbootjpademo.dao.CategoryDAO;
4 import com.example.springbootjpademo.pojo.Category;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.data.domain.Page;
7 import org.springframework.data.domain.PageRequest;
8 import org.springframework.data.domain.Pageable;
9 import org.springframework.data.domain.Sort;
10 import org.springframework.stereotype.Controller;
11 import org.springframework.ui.Model;
12 import org.springframework.web.bind.annotation.*;
13
14 import java.util.List;
15 //@Controller
16 @RestController
17 public class CategoryController {
18     @Autowired
19     CategoryDAO categoryDAO;
20
21     @GetMapping("/category")
22     public List<Category> listCategory(@RequestParam(value = "start", defaultValue = "0") int start,@RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
23         start = start<0?0:start;
24         Sort sort = new Sort(Sort.Direction.DESC, "id");
25         Pageable pageable = new PageRequest(start, size, sort);
26         Page<Category> page =categoryDAO.findAll(pageable);
27         return page.getContent();
28     }
29
30     @GetMapping("/category/{id}")
31     public Category getCategory(@PathVariable("id") int id) throws Exception {
32         Category c= categoryDAO.getOne(id);
33         System.out.println(c);
34         return c;
35     }
36     @PutMapping("/category")
37     public void addCategories(@RequestBody Category category) throws Exception {
38         Category category1=new Category();
39         category1.setName(category.getName());
40         categoryDAO.save(category1);
41         System.out.println("springboot接受到浏览器以JSON格式提交的数据:"+category);
42     }
43 }

3.submit.html

1 <!DOCTYPE html>
2 <html>
3 <head>
4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5     <title>用AJAX以JSON方式提交数据</title>
6     <script type="text/javascript" src="js/jquery.min.js"></script>
7 </head>
8 <body>
9 <form >
10     id:<input type="text" id="id" value="123" /><br/>
11     名称:<input type="text" id="name" value="category xxx"/><br/>
12     <input type="button" value="提交" id="sender" />
13 </form>
14 <div id="messageDiv"></div>
15
16 <script>
17     $('#sender').click(function(){
18         var id=document.getElementById('id').value;
19         var name=document.getElementById('name').value;
20         var category={"name":name,"id":id};
21         var jsonData = JSON.stringify(category);
22         var page="category";
23
24         $.ajax({
25             type:"put",
26             url: page,
27             data:jsonData,
28             dataType:"json",
29             contentType : "application/json;charset=UTF-8",
30             success: function(result){
31             }
32         });
33         alert("提交成功,请在springboot控制台查看服务端接收到的数据");
34
35     });
36 </script>
37 </body>
38
39 </html>

4.getOne.html

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5 <title>用AJAX以JSON方式获取数据</title>
6 <script type="text/javascript" src="js/jquery.min.js"></script>
7 </head>
8 <body>
9     <input type="button" value="通过AJAX获取一个Hero对象---" id="sender">
10
11     <div id="messageDiv"></div>
12
13     <script>
14     $('#sender').click(function(){
15         var url="category/12";
16         $.get(
17                 url,
18                 function(data) {
19                     console.log(data);
20                      var json=data;
21                      var name =json.name;
22                      var id = json.id;
23                      $("#messageDiv").html("分类id:"+ id + "<br>分类名称:" +name );
24
25          });
26     });
27     </script>
28 </body>
29 </html>

5.getMany.html

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5 <title>用AJAX以JSON方式获取数据</title>
6 <script type="text/javascript" src="js/jquery.min.js"></script>
7 </head>
8 <body>
9     <input type="button" value="通过AJAX获取多个分类对象" id="sender">
10
11     <div id="messageDiv"></div>
12
13     <script>
14     $('#sender').click(function(){
15         var url="category?start=0&size=100";
16         $.get(
17                 url,
18                 function(data) {
19                     var categorys = data;
20                      for(i in categorys){
21                          var old = $("#messageDiv").html();
22                          var category = categorys[i];
23                          $("#messageDiv").html(old + "<br>"+category.id+"   -----   "+category.name);
24                      }
25          });
26     });
27     </script>
28 </body>
29
30 </body>
31 </html>

6.直接调试,即采用AJAX的方式。也可采用Postman,结果如下:

<1>请求http://localhost:8080/category,应返回5条记录,默认分页大小为5。注意请求的方式必须是GET,否则会报错。

<2>请求http://localhost:8080/category/12,返回id=12的对象。

<3>获取全部数据http://localhost:8080/category?start=0&size=100

7.代码:https://github.com/lyj8330328/springboot-jpa-demo

四、总结

1.访问方式

通过http://localhost:8080/submit.html这种形式访问视图,必须将视图文件放入src/main目录下的webapp文件夹中,这样才能访问到视图,放在resources下的templates中就会出错。

2.注解

<1>@JsonIgnoreProperties({ "handler","hibernateLazyInitializer" })

因为懒加载这个对象属性只是一个代理对象,如果json直接当作一个存在的属性去序列化就会出现错误。

<2>@RestController

@RestController is a stereotype annotation that combines @ResponseBody and @Controller.
意思是:
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。

1)如果只是使用@RestController注解Controller,则Controller中的方法无法返回视图,返回的内容就是Return 里的内容。

2)如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
3)如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。

posted @ 2018-07-15 18:24 雨落忧伤- 阅读(...) 评论(...) 编辑 收藏
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: