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:

          • GET /getUser/1
          • POST /createUser
          • PUT /updateUser/1
          • DELETE /deleteUser/1

二、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。

 package com.example.springbootrestfuldemo.controller;

 import com.example.springbootrestfuldemo.dao.CategoryDAO;
import com.example.springbootrestfuldemo.pojo.Category;
import com.sun.org.apache.xpath.internal.operations.Mod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import java.util.List; @Controller
public class CategoryController {
@Autowired
CategoryDAO categoryDAO; //restful风格 //1.返回5条记录
@GetMapping("/category")
public String listCategory(Model model,@RequestParam(value = "start", defaultValue = "0") int start, @RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
start = start<0?0:start;
Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable pageable = new PageRequest(start, size, sort);
Page<Category> page =categoryDAO.findAll(pageable);
model.addAttribute("page",page);
return "listCategories";
}
//2.保存一条记录
@PutMapping("/category")
public String addCategories(Category category) throws Exception {
System.out.println("保存一条记录");
categoryDAO.save(category);
return "redirect:/category";
}
//3.删除一条记录
@DeleteMapping("/category/{id}")
public String deleteCategory(Category category){
System.out.println("删除一条记录!");
categoryDAO.delete(category);
return "redirect:/category";
} //4.更新一条记录
@PostMapping("/category/{id}")
public String updateCategory(Category category){
System.out.println("更新一条记录!");
categoryDAO.save(category);
return "redirect:/category";
}
//6.跳转到编辑页
@GetMapping("/category/{id}")
public String addCategory(@PathVariable("id") int id, Model model) throws Exception{
System.out.println("编辑视图");
Category category=categoryDAO.getOne(id);
model.addAttribute("c",category);
return "editCategory";
} }

2.listCategories.html

 <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Title</title>
</head>
<body>
<div class="showing">
<h2>springboot+jpa+thymeleaf+Restful</h2>
<div style="width:500px;margin:20px auto;text-align: center">
<table align="center" border="1" cellspacing="0">
<thead>
<tr>
<th>id</th>
<th>name</th>
<td>编辑</td>
<td>删除</td>
</tr>
</thead>
<tbody>
<tr th:each="c: ${page}">
<td align="center" th:text="${c.id}"></td>
<td align="center" th:text="${c.name}"></td>
<td align="center" ><a th:href="@{/category/{id}(id=${c.id})}">编辑</a></td>
<td>
<form th:action="@{/category/{id}(id=${c.id})}" action="/category" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<button type="submit">删除</button>
</form>
</td>
</tr>
</tbody>
</table>
<br />
<div>
<a th:href="@{/category(start=0)}">[首 页]</a>
<a th:href="@{/category(start=${page.number -1})}">[上一页]</a>
<a th:if="${page.number!=page.totalPages -1}" th:href="@{/category(start=${page.number +1})}">[下一页]</a>
<a th:href="@{/category(start=${page.totalPages -1})}">[末 页]</a>
</div>
<form action="/category" method="post">
<!--修改提交方式为PUT-->
<input type="hidden" name="_method" value="PUT"/>
name:<input name="name"/><br/>
<button type="submit">提交</button>
</form> </div>
</div>
</body>
</html>

<1>增加

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

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

<2>删除

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

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

<3>修改

注意thymeleaf构建url的方式

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

3.editCategory.html

修改action中的地址

 <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Title</title>
</head>
<body>
<div class="showing">
<h2>springboot+jpa</h2> <div style="margin:0px auto; width:500px"> <form th:action="@{/category/{id}(id=${c.id})}" method="post"> name: <input name="name" th:value="${c.name}" /> <br/> <input name="id" type="hidden" th:value="${c.id}" />
<button type="submit">提交</button> </form>
</div>
</div>
</body>
</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" }) ,否则会出错

 package com.example.springbootjpademo.pojo;

 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

 import javax.persistence.*;

 @Entity
@Table(name = "category")
@JsonIgnoreProperties({ "handler","hibernateLazyInitializer" })
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id; @Column(name = "name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} public String toString() {
return "Category [id=" + id + ", name=" + name + "]";
}
}

2.CategoryController.java

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

 package com.example.springbootjpademo.controller;

 import com.example.springbootjpademo.dao.CategoryDAO;
import com.example.springbootjpademo.pojo.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import java.util.List;
//@Controller
@RestController
public class CategoryController {
@Autowired
CategoryDAO categoryDAO; @GetMapping("/category")
public List<Category> listCategory(@RequestParam(value = "start", defaultValue = "0") int start,@RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
start = start<0?0:start;
Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable pageable = new PageRequest(start, size, sort);
Page<Category> page =categoryDAO.findAll(pageable);
return page.getContent();
} @GetMapping("/category/{id}")
public Category getCategory(@PathVariable("id") int id) throws Exception {
Category c= categoryDAO.getOne(id);
System.out.println(c);
return c;
}
@PutMapping("/category")
public void addCategories(@RequestBody Category category) throws Exception {
Category category1=new Category();
category1.setName(category.getName());
categoryDAO.save(category1);
System.out.println("springboot接受到浏览器以JSON格式提交的数据:"+category);
}
}

3.submit.html

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用AJAX以JSON方式提交数据</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<form >
id:<input type="text" id="id" value="123" /><br/>
名称:<input type="text" id="name" value="category xxx"/><br/>
<input type="button" value="提交" id="sender" />
</form>
<div id="messageDiv"></div> <script>
$('#sender').click(function(){
var id=document.getElementById('id').value;
var name=document.getElementById('name').value;
var category={"name":name,"id":id};
var jsonData = JSON.stringify(category);
var page="category"; $.ajax({
type:"put",
url: page,
data:jsonData,
dataType:"json",
contentType : "application/json;charset=UTF-8",
success: function(result){
}
});
alert("提交成功,请在springboot控制台查看服务端接收到的数据"); });
</script>
</body> </html>

4.getOne.html

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>用AJAX以JSON方式获取数据</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<input type="button" value="通过AJAX获取一个Hero对象---" id="sender"> <div id="messageDiv"></div> <script>
$('#sender').click(function(){
var url="category/12";
$.get(
url,
function(data) {
console.log(data);
var json=data;
var name =json.name;
var id = json.id;
$("#messageDiv").html("分类id:"+ id + "<br>分类名称:" +name ); });
});
</script>
</body>
</html>

5.getMany.html

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>用AJAX以JSON方式获取数据</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<input type="button" value="通过AJAX获取多个分类对象" id="sender"> <div id="messageDiv"></div> <script>
$('#sender').click(function(){
var url="category?start=0&size=100";
$.get(
url,
function(data) {
var categorys = data;
for(i in categorys){
var old = $("#messageDiv").html();
var category = categorys[i];
$("#messageDiv").html(old + "<br>"+category.id+" ----- "+category.name);
}
});
});
</script>
</body> </body>
</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注解。

笔记67 Spring Boot快速入门(七)的更多相关文章

  1. 笔记61 Spring Boot快速入门(一)

    IDEA+Spring Boot快速搭建 一.IDEA创建项目 略 项目创建成功后在resources包下,属性文件application.properties中,把数据库连接属性加上,同时可以设置服 ...

  2. 笔记65 Spring Boot快速入门(五)

    SpringBoot+JPA 一.什么是JPA? JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期 ...

  3. 笔记63 Spring Boot快速入门(三)

    SpringBoot中使用JSP Springboot的默认视图支持是Thymeleaf,但是Thymeleaf还没开始学,熟悉的还是jsp,所以要让Springboot支持 jsp. 一.在pom. ...

  4. 笔记70 Spring Boot快速入门(八)(重要)

    上传文件 一.方式一 1.上传页面 upLoadPage.html <!DOCTYPE html> <html lang="en"> <head> ...

  5. 笔记64 Spring Boot快速入门(四)

    SpringBoot中错误处理.端口设置和上下文路径以及配置切换 一.错误处理 假设在访问首页的时候会出现一些错误,然后将这些错误当作异常抛出,反馈给用户. 1.修改IndexController.j ...

  6. 笔记62 Spring Boot快速入门(二)

    SpringBoot部署 一.jar方式 1.首先安装maven. <1>下载最新的maven版本:https://maven.apache.org/download.cgi <2& ...

  7. 笔记66 Spring Boot快速入门(六)

    SpringBoot中使用Mybatis 一.注解方式 1.创建映射文件CategoryMapper.java 使用注解@Mapper 表示这是一个Mybatis Mapper接口.使用@Select ...

  8. Spring Boot 快速入门

    Spring Boot 快速入门 http://blog.csdn.net/xiaoyu411502/article/details/47864969 今天给大家介绍一下Spring Boot MVC ...

  9. Spring Boot快速入门(二):http请求

    原文地址:https://lierabbit.cn/articles/4 一.准备 postman:一个接口测试工具 创建一个新工程 选择web 不会的请看Spring Boot快速入门(一):Hel ...

随机推荐

  1. find 文件查找

    目录 find文件查找 1.为什么要使用文件查找 2.根据文件名称查找-name 3.根据文件大小查找-size 4.根据文件类型查找-type f 5.根据文件时间查找-mtime 6.根据文件用户 ...

  2. Jmeter 将正则表达式提取的参数传给全局(跨线程组使用变量)

    一.使用正则表达式提取sessionId 1.在测试计划(跨线程组使用变量)--> 线程组(登录)--> 添加HTTP请求(登录接口) (1)创建测试计划: 勾选独立运行每个线程组(例如在 ...

  3. Oracle查询用户所有表

    https://blog.csdn.net/wssiqi/article/details/44617197 Oracle查询用户所有表   下面为您介绍的语句用于实现Oracle查询用户所有表,如果您 ...

  4. OpenLayers API整理

    整理的Openlayers 的知识笔记,随着运用不断加深理解,也会不断更新. 本文链接:Openlayers API整理 作者:狐狸家的鱼 GitHub:八至 一.创建地图 1.地图Map 创建地图底 ...

  5. nginx支持webSocket ws请求

    服务端webSocket的java配置文件: @Override public void registerStompEndpoints(StompEndpointRegistry registry) ...

  6. 【InnoDB】插入缓存,两次写,自适应hash索引

    InnoDB存储引擎的关键特性包括插入缓冲.两次写(double write).自适应哈希索引(adaptive hash index).这些特性为InnoDB存储引擎带来了更好的性能和更高的可靠性. ...

  7. QUIC协议学习记录

    QUIC(Quick UDP Internet Connections,快速UDP互联网连接)是Google提出的一种基于UDP改进的通信协议,其目的是降低网络通信的延迟,提供更好的用户互动体验. Q ...

  8. 一条SQL在 MaxCompute 分布式系统中的旅程

    摘要:2019杭州云栖大会大数据技术专场,由阿里云资深技术专家侯震宇.阿里云高级技术专家陈颖达以及阿里云资深技术专家戴谢宁共同以“SQL在 MaxCompute 分布式系统中的旅程 ”为题进行了演讲. ...

  9. FTP上传脚本

    脚本名称:uploadToFtp.sh 脚本用途:上传文件到FTP服务器 脚本参数:$1:FTP服务器的路径,$2:本地文件 #!/bin/bash FTP_USER="testfile&q ...

  10. Python基础教程(003)--Python的设计目标

    前言 了解Python的设计目标,这节是了解Python的背景,不需要掌握,但是需要你知道有这个东西.就是Python的目的是什么.作者开发这个Python语言的目的是什么. 知识点 一门简单直观的语 ...