【接口设计】为 APP、PC、H5 网页提供统一风格的 API(实战篇,附源码地址)

接口设计》系列,共包含以下 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 修饰;如果强制指定其他的访问控制符(例如 publicprocted 等),则会报错。
🚀 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不可能产生实例。列出的这些实例,系统会自动给它们加上 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 实现返回的对象实体

实现返回的 对象实体,返回 CodeMessage(信息),见以下代码:

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);
}

在这里插入图片描述

基于Node.js,提供网易云所有API接口数据,包括:登录,获取用户信息 , 歌单,收藏,mv, dj 数量 获取用户歌单 获取用户电台 获取用户关注列表 获取用户粉丝列表 获取用户动态 获取用户播放记录 获取精品歌单 获取歌单详情 搜索 搜索建议 获取歌词 歌曲评论 收藏单曲到歌单 专辑评论 歌单评论 mv 评论 电台节目评论 banner 获取歌曲详情 获取专辑内容 获取歌手单曲 获取歌手 mv 获取歌手专辑 获取歌手描述 获取相似歌手 获取相似歌单 相似 mv 获取相似音乐 获取最近 5 个听了这首歌的用户 获取每日推荐歌单 获取每日推荐歌曲 私人 FM 签到 喜欢音乐 垃圾桶 歌单 ( 网友精选碟 ) 新碟上架 热门歌手 最新 mv 推荐 mv 推荐歌单 推荐新音乐 推荐电台 推荐节目 独家放送 mv 排行 获取 mv 数据 播放 mv/视频 排行榜 歌手榜 云盘 电台 - 推荐 电台 - 分类 电台 - 分类推荐 电台 - 订阅 电台 - 详情 电台 - 节目 给评论点赞 获取动态 热搜列表(简略) 发送私信 发送私信歌单 新建歌单 收藏/取消收藏歌单 歌单分类 收藏的歌手列表 订阅的电台列表 相关歌单推荐 付费精选接口 音乐是否可用检查接口 登录状态 获取视频播放地址 发送/删除评论 热门评论 视频评论 退出登录 所有榜单 所有榜单内容摘要 收藏视频 收藏 MV 视频详情 相关视频 关注用户 新歌速递 喜欢音乐列表(无序) 收藏的 MV 列表 获取最新专辑 听歌打卡 获取视频标签下的视频 已收藏专辑列表 获取动态评论 歌单收藏者列表 云盘歌曲删除 热门话题 电台 - 推荐类型 电台 - 非热门类型 电台 - 今日优选 心动模式/智能播放 转发动态 删除动态 分享歌曲、歌单、mv、电台、电台节目到动态 通知-私信 通知-评论 通知-@我 通知-通知 设置 云盘数据详情 私信内容 我的数字专辑 batch批量请求接口 获取视频标签列表 全部mv 网易出品mv 收藏/取消收藏专辑 专辑动态信息 热搜列表(详细) 更换绑定手机 检测手机号码是否已注册 初始化昵称 更新歌单描述 更新歌单名 更新歌单标签 默认搜索关键词 删除歌单 电台banner 用户电台 热门电台 电台 - 节目详情 电台 - 节目榜 电台 - 新晋电台榜/热门电台榜 类别热门电台 云村热评 电台24小时节目榜 电台24小时主播榜 电台最热主播榜 电台主播新人榜 电台付费精品榜 歌手热门50首歌曲
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

G皮T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值