为 APP、PC、H5 网页提供统一风格的 API
- 1.实现文章实体
- 2.实现数据持久层
- 3.实现服务接口和服务接口的实现类
- 3.1 创建服务接口
- 3.2 编写服务接口的实现
- 4.处理返回结果
- 4.1 实现响应的枚举类
- 4.2 实现返回的对象实体
- 4.3 封装返回结果
- 4.统一处理异常
- 4.1 全局捕捉异常
- 4.2 自定义异常类
- 5.控制器
- 5.1 自定义业务测试控制器
- 5.2 实现数据的 CRUD 控制器
- 6.测试数据
- 6.1 添加数据
- 6.2 获取数据列表
- 6.3 查询数据
- 6.3 修改数据
- 6.4 删除数据
- 6.5 访问不存在的数据
项目的目录结构如下:
🚀 GitHub 地址:https://github.com/Beracle/RESTful-Example
1.实现文章实体
package com.example.demo.entity;
import lombok.Data;
import lombok.Getter;
import javax.persistence.*;
@Entity
@Table(name = "article")
@Data
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String title;
private String body;
}
2.实现数据持久层
package com.example.demo.repository;
import com.example.demo.entity.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ArticleRepository extends JpaRepository<Article,Long>, JpaSpecificationExecutor<Article> {
Article findById(long id);
}
3.实现服务接口和服务接口的实现类
3.1 创建服务接口
package com.example.demo.service;
import com.example.demo.entity.Article;
import java.util.List;
public interface ArticleService {
public List<Article> getArticleList();
public Article findArticleById(long id);
}
3.2 编写服务接口的实现
package com.example.demo.service.impl;
import com.example.demo.entity.Article;
import com.example.demo.repository.ArticleRepository;
import com.example.demo.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleRepository articleRepository;
@Override
public List<Article> getArticleList() {
return articleRepository.findAll();
}
@Override
public Article findArticleById(long id) {
return articleRepository.findById(id);
}
}
4.处理返回结果
4.1 实现响应的枚举类
枚举 是一种特殊的数据类型,它是一种 “类类型”,比类型多了一些特殊的约束。创建枚举类型要使用 enum
,表示所创建的类型都是 java.lang.Enum
类(抽象类)的子类。
🚀 枚举类默认继承
java.lang.Enum
类,而不是 Object 类,因此枚举类不能显式继承其他父类。
🚀 枚举类的构造器只能使用private
访问控制符,如果忽略访问控制符的话,则默认使用private
修饰;如果强制指定其他的访问控制符(例如public
、procted
等),则会报错。
🚀 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不可能产生实例。列出的这些实例,系统会自动给它们加上public static final
修饰(无需程序员手动添加)。枚举类的实例 以逗号分隔,分号结束,这些列出的枚举值代表了该枚举类的所有可能的实例。
package com.example.demo.result;
// 实现响应的枚举类
public enum ExceptionMsg {
SUCCESS("200", "操作成功"),
FAILED("999999", "操作失败"),
ParamError("000001", "参数错误!"),
FileEmpty("000400", "上传文件为空"),
LimitPictureSize("000401", "图片大小必须小于2M"),
LimitPictureType("000402", "图片格式必须为'jpg'、'png'、'jpge'、'gif'、'bmp'");
private String code;
private String msg;
// 自定义构造函数
private ExceptionMsg(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
4.2 实现返回的对象实体
实现返回的 对象实体,返回 Code
和 Message
(信息),见以下代码:
package com.example.demo.result;
// 实现返回对象实体
public class Response {
// 返回信息码
private String rspCode = "000000";
// 返回信息内容
private String rspMsg = "操作成功";
public Response() {
}
public Response(ExceptionMsg msg) {
this.rspCode = msg.getCode();
this.rspMsg = msg.getMsg();
}
public Response(String rspCode) {
this.rspCode = rspCode;
this.rspMsg = "";
}
public Response(String rspCode, String rspMsg) {
this.rspCode = rspCode;
this.rspMsg = rspMsg;
}
public String getRspCode() {
return rspCode;
}
public void setRspCode(String rspCode) {
this.rspCode = rspCode;
}
public String getRspMsg() {
return rspMsg;
}
public void setRspMsg(String rspMsg) {
this.rspMsg = rspMsg;
}
@Override
public String toString() {
return "Response{" +
"rspCode='" + rspCode + '\'' +
", rspMsg='" + rspMsg + '\'' +
'}';
}
}
4.3 封装返回结果
这里把返回的结果进行封装,以显示 数据,见以下代码:
package com.example.demo.result;
// 返回结果数据格式封装
public class ResponseData extends Response {
private Object data;
public ResponseData(Object data) {
this.data = data;
}
public ResponseData(ExceptionMsg msg) {
super(msg);
}
public ResponseData(String rspCode, String rspMsg) {
super(rspCode, rspMsg);
}
public ResponseData(String rspCode, String rspMsg, Object data) {
super(rspCode, rspMsg);
this.data = data;
}
public ResponseData(ExceptionMsg msg, Object data) {
super(msg);
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "ResponseData{" +
"data=" + data +
"} " + super.toString();
}
}
4.统一处理异常
4.1 全局捕捉异常
package com.example.demo.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@RestControllerAdvice
public class GlobalExceptionHandler {
// 日志记录工具
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public Map<String, Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
logger.error("缺少请求参数", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 400);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Map<String, Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
logger.error("参数解析失败", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 400);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
logger.error("参数验证失败", e);
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
String field = error.getField();
String code = error.getDefaultMessage();
String message = String.format("%s:%s", field, code);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 400);
map.put("rspMsg", message);
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public Map<String, Object> handleBindException(BindException e) {
logger.error("参数绑定失败", e);
Map<String, Object> map = new HashMap<String, Object>();
BindingResult result = e.getBindingResult();
FieldError error = result.getFieldError();
String field = error.getField();
String code = error.getDefaultMessage();
String message = String.format("%s:%s", field, code);
map.put("rspCode", 400);
map.put("rspMsg", message);
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public Map<String, Object> handleServiceException(ConstraintViolationException e) {
logger.error("参数验证失败", e);
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String message = violation.getMessage();
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 400);
map.put("rspMsg", message);
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
public Map<String, Object> handleValidationException(ValidationException e) {
logger.error("参数验证失败", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 400);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 405 - Method Not Allowed
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Map<String, Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
logger.error("不支持当前请求方法", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 405);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 415 - Unsupported Media Type
*/
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Map<String, Object> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
logger.error("不支持当前媒体类型", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 415);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 自定义异常类
*/
@ResponseBody
@ExceptionHandler(BusinessException.class)
public Map<String, Object> businessExceptionHandler(BusinessException e) {
logger.error("自定义业务失败", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", e.getCode());
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
/**
* 获取其它异常。包括500
*
* @param e
* @return
* @throws Exception
*/
@ExceptionHandler(value = Exception.class)
public Map<String, Object> defaultErrorHandler(Exception e) {
logger.error("Exception", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rspCode", 500);
map.put("rspMsg", e.getMessage());
//发生异常进行日志记录,写入数据库或者其他处理,此处省略
return map;
}
}
4.2 自定义异常类
package com.example.demo.exception;
public class BusinessException extends RuntimeException {
//自定义错误码
private Integer code;
// 自定义构造器,必须输入错误码及内容
public BusinessException(int code, String msg) {
super(msg);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
5.控制器
5.1 自定义业务测试控制器
package com.example.demo.controller;
import com.example.demo.exception.BusinessException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.ValidationException;
@RestController
public class TestController {
@RequestMapping("/BusinessException")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i) {
if (i == 0) {
throw new BusinessException(600, "自定义业务错误");
}
throw new ValidationException();
}
}
5.2 实现数据的 CRUD 控制器
实现数据的 增加、删除、修改 和 删除 控制器,并实现数据的返回,见以下代码:
package com.example.demo.controller;
import com.example.demo.entity.Article;
import com.example.demo.repository.ArticleRepository;
import com.example.demo.result.ExceptionMsg;
import com.example.demo.result.Response;
import com.example.demo.result.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("article")
public class ArticleController {
protected Response result(ExceptionMsg msg) {
return new Response(msg);
}
protected Response result() {
return new Response();
}
@Autowired
private ArticleRepository articleRepository;
@Autowired
RestTemplateBuilder restTemplateBuilder;
@RequestMapping(value = "/", method = RequestMethod.GET)
public ResponseData getArticleList() {
List<Article> list = new ArrayList<Article>(articleRepository.findAll());
return new ResponseData(ExceptionMsg.SUCCESS, list);
}
//增
@RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseData add(Article article) {
articleRepository.save(article);
return new ResponseData(ExceptionMsg.SUCCESS, article);
}
//删
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Response delete(@PathVariable("id") long id) {
articleRepository.deleteById(id);
return result(ExceptionMsg.SUCCESS);
}
//改
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseData update(Article model) {
articleRepository.save(model);
return new ResponseData(ExceptionMsg.SUCCESS, model);
}
//查
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseData findArticle(@PathVariable("id") Integer id) throws IOException {
Article article = articleRepository.findById(id);
if (article != null) {
return new ResponseData(ExceptionMsg.SUCCESS, article);
}
return new ResponseData(ExceptionMsg.FAILED, article);
}
//查
@RequestMapping(value = "/re/{id}", method = RequestMethod.GET)
public Article findArticled(@PathVariable("id") Integer id) throws IOException {
RestTemplate client = restTemplateBuilder.build();
String uri = "http://localhost:8080/article/" + id;
System.out.println(uri);
Article article = client.getForObject(uri, Article.class, id);
return article;
}
}
6.测试数据
6.1 添加数据
用 POST 方式访问 http://localhost:8080/article/
,提交 Article 实体。
//增
@RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseData add(Article article) {
articleRepository.save(article);
return new ResponseData(ExceptionMsg.SUCCESS, article);
}
6.2 获取数据列表
用 GET 方法访问 http://localhost:8080/article/
。
@RequestMapping(value = "/", method = RequestMethod.GET)
public ResponseData getArticleList() {
List<Article> list = new ArrayList<Article>(articleRepository.findAll());
return new ResponseData(ExceptionMsg.SUCCESS, list);
}
6.3 查询数据
查询刚刚添加的数据。用 GET 方法访问 http://localhost:8080/article/1
。
//查
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseData findArticle(@PathVariable("id") Integer id) throws IOException {
Article article = articleRepository.findById(id);
if (article != null) {
return new ResponseData(ExceptionMsg.SUCCESS, article);
}
return new ResponseData(ExceptionMsg.FAILED, article);
}
6.3 修改数据
用 PUT 方法访问 http://localhost:8080/article/1
。
//改
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseData update(Article model) {
articleRepository.save(model);
return new ResponseData(ExceptionMsg.SUCCESS, model);
}
6.4 删除数据
用 DELETE 方法访问 http://localhost:8080/article/1
。
//删
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Response delete(@PathVariable("id") long id) {
articleRepository.deleteById(id);
return result(ExceptionMsg.SUCCESS);
}
如果返回以下结果,则代表删除成功。
6.5 访问不存在的数据
用 GET 方法访问 http://localhost:8080/article/0
。
// 查
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseData findArticle(@PathVariable("id") Integer id) throws IOException {
Article article = articleRepository.findById(id);
if (article != null) {
return new ResponseData(ExceptionMsg.SUCCESS, article);
}
return new ResponseData(ExceptionMsg.FAILED, article);
}