背景
什么是REST(来自百度百科)
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。
理解RESTful
要理解RESTful架构,需要理解Representational State Transfer这个词组到底是什么意思,它的每一个词都有些什么涵义。REST基本原则。
*资源与URI
*统一资源接口
*资源的表述
*资源的链接
*状态的转移
环境
*jdk 1.8
*spring boot 2.1.6
*maven 4.0.0
*intellj idea 2018.2
*window 10
配置
实操
目录结构
在上一版增加了user模块,主要用来处理用户信息相关的业务
源码
主要是为了学习接口,不引入数据操作,Dao层模拟下数据库的操作输出即可。
平台用户基本信息实体(Member.java)
package com.springboot.action.saas.modules.user.po;
import lombok.Data;
/*
* 平台用户信息数据
* */
//这里使用@Data注解,是lombok包中的,自动生成一些操作成员变量的方法
@Data
public class Member {
//用户id
private Integer id;
//用户手机号
private String phone;
//用户密码
private String password;
//用户昵称
private String nickname;
//邮箱
private String email;
//注册时间
private Integer ctime;
//更新时间
private Integer utime;
//最近登录时间
private Integer last_login_time;
//最近登录ip
private String last_login_ip;
//邀请码
private String invite_code;
//激活(绑定手机)状态标示
private Integer is_active;
//删除状态标识
private Integer is_delete;
}
数据访问对象(MemberDao.java),这里为了循序渐进的学习,先不引入数据的操作,先模拟记录下
package com.springboot.action.saas.modules.user.dao;
import com.springboot.action.saas.modules.user.po.Member;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class MemberDao {
private Member testmember;
public void add(Member member){
System.out.println("MemberDao.add");
testmember = new Member();
testmember.setId(1);
testmember.setNickname("demo");
}
public void save(Member member){
System.out.println("MemberDao.save");
testmember.setNickname(member.getNickname());
}
public void delete(Integer id){
System.out.println("MemberDao.delete"+id);
}
public Member findOne(Integer id) {
System.out.println("MemberDao.findOne"+id);
return testmember;
}
public List findAll() {
System.out.println("MemberDao.findAll");
List list = new ArrayList();
Member member1 = new Member();
member1.setId(1);
member1.setNickname("demo1");
list.add(member1);
Member member2 = new Member();
member2.setId(2);
member2.setNickname("demo2");
list.add(member2);
return list;
}
}
事务处接口定义(MemberService.java)
package com.springboot.action.saas.modules.user.service;
import com.springboot.action.saas.modules.user.po.Member;
import java.util.List;
/*
* 业务接口定义
* */
public interface MemberService {
public void addMember(Member member);
public void deleteMember(Integer id);
public void updateMember(Member member);
public Member findMember(Integer id);
public List findAllMember();
}
事务处接口实现类(MemberServiceImpl.java)
package com.springboot.action.saas.modules.user.service.impl;
import com.springboot.action.saas.modules.user.dao.MemberDao;
import com.springboot.action.saas.modules.user.po.Member;
import com.springboot.action.saas.modules.user.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*
* 业务接口是实现
* */
@Service
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberDao memberDao;
@Override
public void addMember(Member member) {
memberDao.add(member);
}
@Override
public void deleteMember(Integer id) {
memberDao.delete(id);
}
@Override
public void updateMember(Member member) {
memberDao.save(member);
}
@Override
public Member findMember(Integer id) {
return memberDao.findOne(id);
}
@Override
public List findAllMember() {
return memberDao.findAll();
}
}
控制层实现(MemberController.java)
package com.springboot.action.saas.modules.user.controller;
import com.springboot.action.saas.modules.user.po.Member;
import com.springboot.action.saas.modules.user.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/*
* restful 风格接口
* */
//@RestController 代替 @Controller,省略以后的 @ResponseBody
@RestController
//处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestMapping("/member")
public class MemberController {
@Autowired
private MemberService memberService;
/**
* 显示所有Member,请求url:"http://xxx/member/v1/findall"
*
* @return List
*/
@RequestMapping(value = "/v1/findall")
public List findAllMember() {
return memberService.findAllMember();
}
/**
* 查找id对应的Member信息,请求url:"http://xxx/member/v1/findone/1"
*
* @param id
* @return Member
*/
// == @RequestMapping(value = "/v1/findone/{id}", method = RequestMethod.GET)
@GetMapping("/v1/findone/{id}")
public Member findMember(@PathVariable("id") Integer id) {
return memberService.findMember(id);
}
/**
* 删除id对应的Member,请求url:"http://xxx/member/v1/deleteone/4"
* 可以通过 jquery的 $.ajax者postman方法,并type="delete"
*
* @param id
*
* @return void
*/
// == @RequestMapping(value = "/v1/deleteone/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/v1/deleteone/{id}")
public void deleteMember(@PathVariable("id") Integer id) {
memberService.deleteMember(id);
}
/**
* 增加member信息,请求url:"http://xxx/member/v1/addone"
* 数据通过
运行测试输出
获取全部用户列表
打tag 1.0.2版本,提交代码。
git tag -a v1.0.2 -m "实现用户模块简单的rest风格接口"
git push origin v1.0.2
github地址:https://github.com/horacepei/springsaas
正确/出错/异常返回数据格式统一
如果出现类型不对的请求或者参数错误会报错,实际上客户端是期望返回统一格式返回值。实现原理,可以在接口方法返回之后,在序列化器对该结果进行序列化之前,我们可以使用ResponseBodyAdvice在这两个阶段之间对结果进行操作,因此我们可以使用该Adivice在序列化之前,拦截返回结果,对结果进行包装,然后再将包装后的结果返回,最后序列化成对应的JSON字符串。
统一返回数据格式(RestReturn.java)
package com.springboot.action.saas.common.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.time.Instant;
/**
* 接口返回统一数据结构
*/
@Data
public class RestReturn {
//是否成功标志
private boolean success;
//code错误码
private Integer code;
//外带数据信息
private Object data;
//前端进行页面展示的信息
private Object message;
//返回时间戳
private Long currentTime;
/**
*构造函数(无参数)
*/
public RestReturn() {
//毫秒
this.currentTime = Instant.now().toEpochMilli();
}
/**
*构造函数(有参数)
*/
public RestReturn(boolean success, Integer code, Object data, Object message) {
this.success = success;
this.code = code;
this.data = data;
this.message = message;
//毫秒
this.currentTime = Instant.now().toEpochMilli();
}
@Override
public String toString() {
return "RestReturn{" +
"success=" + success +
",code='" + code + '\'' +
",data=" + data +
",message=" + message +
",currentTime=" + currentTime +
'}';
}
public RestReturn success(Object data, Object message) {
this.success = true;
this.code = 0;
this.data = data;
this.message = message;
return this;
}
public RestReturn error(Integer code, Object data, Object message) {
this.success = false;
this.code = code;
this.data = data;
this.message = message;
return this;
}
public boolean isRestReturnJson(String data) {
//临时实现先判定下字符串的格式和字段
try {
/**
* ObjectMapper支持从byte[]、File、InputStream、字符串等数据的JSON反序列化。
*/
ObjectMapper mapper = new ObjectMapper();
RestReturn dataRestReturn = mapper.readValue(data, RestReturn.class);
//比较两个类的字段,如果一致返回为真,不一致返回为假
return true;
} catch (Exception e) {
return false;
}
}
}
返回数据拦截器的封装(MemberController.java)
package com.springboot.action.saas.common.controller;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class RestReturnWrapper implements ResponseBodyAdvice
打tag 1.0.3版本,提交代码。
git tag -a v1.0.3 -m "正确/出错/异常返回格式统一处理"
git push origin v1.0.3
github地址:https://github.com/horacepei/springsaas