前后端分离传值方案-RestfulAPI

SpringBoot结合Swagger展示Restful API

标签(空格分隔): springboot java springmvc jpa


镇楼

Restful API 搭建

RESTful架构,是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。后端通过提供一套标准的RESTful API,让网站,移动端和第三方系统都可以基于API进行数据交互和对接,极大的提高系统的开发效率,也使得前后端分离架构成为可能。

SpringBoot开发RestfulAPI

maven 3.3.9 ,Mysql 5.7.19,编译器采用idea,本示例实现学生信息的增删改查

  • 数据库表结构设计
CREATE TABLE `stu_information` (
  `stu_id` varchar(100) NOT NULL COMMENT '学生id',
  `stu_name` varchar(10) NOT NULL COMMENT '学生姓名',
  `stu_sex` int(11) NOT NULL COMMENT '学生性别,0表示男,1表示女',
  `stu_age` int(11) NOT NULL COMMENT '学生年龄',
  `stu_magor` varchar(100) NOT NULL COMMENT '专业',
  `stu_grade` int(11) NOT NULL COMMENT '年级',
  `stu_department` varchar(100) NOT NULL COMMENT '院系',
  `stu_class` varchar(100) NOT NULL COMMENT '班级',
  PRIMARY KEY (`stu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';
  • 项目创建(这个就不多讲解了),添加依赖
<!--数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!--jpa-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!--lombok 插件-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

  • 配置文件设置
server:
  port: 8088
  context-path: /student

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/stu_api?characterEncoding=utf-8&useSSL=false
  jpa:
    show-sql: true
  • Api封装
<!--呈现内容-->
@Data
public class ResultVo<T> {

    /** 状态码 .*/
    private Integer code;

    /** 消息 .*/
    private String msg;

    /** 具体值 .*/
    private T data;

}

<!--返回结果工具-->
public class ResultVoUtil {

    /**
     * 成功返回有值
     * @param object
     * @return
     */
    public static ResultVo success(Object object){
        ResultVo resultVo = new ResultVo();
        resultVo.setCode(ResultEnum.SUCCESS.getCode());
        resultVo.setMsg(ResultEnum.SUCCESS.getMessage());
        resultVo.setData(object);
        return resultVo;
    }

    /**
     * 无值成功返回
     * @return
     */
    public static ResultVo success(){
       return success(null);
    }

    /**
     * 错误返回
     * @param resultEnum
     * @return
     */
    public static ResultVo error(ResultEnum resultEnum){
        ResultVo resultVo =  new ResultVo();
        resultVo.setCode(resultEnum.getCode());
        resultVo.setMsg(resultEnum.getMessage());
        return resultVo;
    }
}
  • 返回结果枚举
@Getter
public enum ResultEnum {
    SUCCESS(0,"成功"),
    LIST_SAVE_ERROR(10,"批量存储出错"),
    PARAM_ERROR_ADD(20,"添加学生信息错误")
    ;

    private Integer code;

    private String message;

    ResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
  • 异常处理
public class StuInformationException extends RuntimeException{

    private Integer code;

    public StuInformationException(ResultEnum resultEnum) {
        super(resultEnum.getMessage());
        this.code = resultEnum.getCode();
    }
}
  • 主键生成工具
public class KeyUtil {

    /**
     * 唯一值学生主键生成
     * @return
     */
    public static synchronized String getUniqueKey(){
        Random random = new Random();
        Integer number = random.nextInt(90000)+10000;
        return "2014"+number;
    }
}
  • Dao层处理 Jpa简化操作

@Entity
@Data
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    /** 用户名 .*/
    private String username;

    /** 密码 .*/
    private String password;

    /** 状态 .*/
    private String status;

    /** 描述 .*/
    private String descn;

}

public interface StuInformationRepository extends JpaRepository<StuInformation,String>{

}
  • 表单提交
@Data
public class StudentForm {

    /** 姓名 .*/
    private String stuName;

    /** 性别 0表示男生,1标识女生 .*/
    private Integer stuSex;

    /** 年龄 .*/
    private Integer stuAge;

    /** 专业 .*/
    private String stuMagor;

    /** 年级 .*/
    private Integer stuGrade;

    /** 院系 .*/
    private String stuDepartment;

    /** 班级 .*/
    private String stuClass;
}

<!--提交值集合转换-->
public class StudentFormList2StuInformationList {

    public static StuInformation convert(StudentForm studentForm){
        StuInformation stuInformation = new StuInformation();
        BeanUtils.copyProperties(studentForm,stuInformation);
        stuInformation.setStuId(KeyUtil.getUniqueKey());
        return stuInformation;
    }

    public static List<StuInformation> convert(List<StudentForm> studentForms){
        return studentForms.stream().map(e->convert(e)).collect(Collectors.toList());
    }
}
  • Service层处理
public interface StuInfomationService {

    /** 查询所有 .*/
    Page<StuInformation> findAll(Pageable pageable);

    /** 单个结果查询 .*/
    StuInformation findOne(String stuId);

    /** 集合插入 .*/
    List<StuInformation> save(List<StudentForm> studentForms);

    /** 单个存储 .*/
    StuInformation add(StudentForm form);

    /** 更新 .*/
    StuInformation update(StuInformation stuInformation);

    void delete(String stuid);
}


@Service
@Slf4j
public class StuInformationServiceImpl implements StuInfomationService {

    @Autowired
    private StuInformationRepository repository;


    @Override
    public Page<StuInformation> findAll(Pageable pageable) {
        return repository.findAll(pageable);
    }

    @Override
    public StuInformation findOne(String stuId) {
        return repository.findOne(stuId);
    }

    @Override
    @Transactional
    public List<StuInformation> save(List<StudentForm> studentForms) {
        List<StuInformation> result = repository.save(StudentFormList2StuInformationList.convert(studentForms));
        if(result.size()!=studentForms.size()){
            log.error("【批量数据插入出错】 批量数据插入错误 result = {}",result);
            throw new StuInformationException(ResultEnum.LIST_SAVE_ERROR);
        }
        return result;
    }

    @Override
    public StuInformation add(StudentForm form) {
        StuInformation stuInformation = new StuInformation();
        BeanUtils.copyProperties(form,stuInformation);
        stuInformation.setStuId(KeyUtil.getUniqueKey());
        return repository.save(stuInformation);
    }

    @Override
    public StuInformation update(StuInformation stuInformation) {
        StuInformation match = repository.findOne(stuInformation.getStuId());
        if(match==null){
            log.error("【更新操作】 数据库出错 stuInformation={}",stuInformation);
            throw new StuInformationException(ResultEnum.PARAM_ERROR_ADD);
        }
        StuInformation result = repository.save(stuInformation);
        return result;
    }

    @Override
    public void delete(String stuid) {
        StuInformation stuInformation = repository.findOne(stuid);
        if(stuInformation==null){
            log.error("【更新操作】 数据库出错 stuInformation={}",stuInformation);
            throw new StuInformationException(ResultEnum.PARAM_ERROR_ADD);
        }
        repository.delete(stuid);
    }
}

Swagger可视化API方案

Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。本文简单介绍了在项目中集成swagger的方法和一些常见问题。如果想深入分析项目源码,了解更多内容,见参考资料。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。

  • 依赖添加
<!--api文档规范-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.2.2</version>
</dependency>
  • Swagger配置
@Configuration
@EnableSwagger2
public class Swagger2Config {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.xiaojinzi.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2构建RESTful APIs")
                .description("后期写成md上传简书:https://www.jianshu.com/u/cebf2052c61b")
                .termsOfServiceUrl("https://www.jianshu.com/u/cebf2052c61b")
                .contact("金全")
                .version("1.0")
                .build();
    }
}
  • Controller 请求处理
@RestController
@RequestMapping("/student")
@Slf4j
public class StuInfoController {

    @Autowired
    private StuInfomationService stuInfomationService;

    /**
     * 单个对象添加
     * @param form
     * @param bindingResult
     * @return
     */
    @ApiOperation(value = "添加学生",notes = "根据对象添加")
    @ApiImplicitParam(name = "form",value = "学生实体StudentForm",required = true,dataType = "StudentForm")
    @PostMapping("/add")
    public ResultVo addStudent(@RequestBody StudentForm form, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            log.error("【添加学生】 参数传递错误 form ={}",form);
            throw new StuInformationException(ResultEnum.PARAM_ERROR_ADD);
        }
        StuInformation result = stuInfomationService.add(form);
        return ResultVoUtil.success(result);
    }


    /**
     * 批量添加
     * @param list
     * @param bindingResult
     * @return
     */
    @ApiOperation(value = "批量添加",notes = "集合对象添加")
    @PostMapping("/batchadd")
    public ResultVo batchAdd(@RequestBody List<StudentForm> list,BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            log.error("【添加学生】 参数传递错误 list ={}",list);
            throw new StuInformationException(ResultEnum.PARAM_ERROR_ADD);
        }
        List<StuInformation> stuInformations = stuInfomationService.save(list);
        return ResultVoUtil.success(stuInformations);
    }

    /**
     * 根据单个查询
     * @param stuid
     * @return
     */
    @ApiOperation(value = "单个查询",notes = "id查询")
    @ApiImplicitParam(name = "stuid",value = "通过id查询",required = true,dataType = "String",paramType = "Path")
    @GetMapping("/findone/{stuid}")
    public ResultVo findOne(@PathVariable String stuid){
        Map<String,String> map = new HashMap<>();
        StuInformation stuInformation = stuInfomationService.findOne(stuid);
        if(stuInformation==null){
            map.put("object","没有该同学");
            return ResultVoUtil.success(map);
        }
        return ResultVoUtil.success(stuInformation);
    }

    /**
     * 分页查询
     * @param page
     * @param size
     * @return
     */
    @ApiOperation(value = "分页查询",notes = "分页参数")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page",value = "当前页",required = false,dataType = "Integer",defaultValue = "1",paramType = "Query"),
            @ApiImplicitParam(name = "size",value = "页面显示数",required = false,dataType = "Integer",defaultValue = "10",paramType = "Query")
    })
    @GetMapping("/findAll")
    public ResultVo findAll(@RequestParam(value = "page",defaultValue = "1")Integer page,@RequestParam(value = "size",defaultValue = "10")Integer size){
        PageRequest request = new PageRequest(page-1,size);
        Page<StuInformation> stuInformations = stuInfomationService.findAll(request);
        return ResultVoUtil.success(stuInformations.getContent());
    }

    /**
     * 更新操作
     * @param stuInformation
     * @return
     */
    @ApiOperation(value = "更新操作",notes = "单个更新")
    @ApiImplicitParam(name = "stuInformation",value = "学生实体stuInformation",required = true,dataType = "StuInformation")
    @PutMapping("/update")
    public ResultVo updateStu(@RequestBody StuInformation stuInformation){
        StuInformation result = stuInfomationService.update(stuInformation);
        return ResultVoUtil.success(result);
    }

    /**
     * 删除操作
     * @param stuid
     * @return
     */
    @ApiOperation(value = "删除操作" ,notes = "根据id删除")
    @ApiImplicitParam(name = "stuid",value = "主键删除",required = true,dataType = "String",paramType = "Path")
    @DeleteMapping("/delete/{stuid}")
    public ResultVo deleteStu(@PathVariable String stuid){
        stuInfomationService.delete(stuid);
        return ResultVoUtil.success();
    }

}
  • 截图
成功运行界面
具体api调试

  • 借鉴博客(程序员DD(翟永超)小哥哥构建强大的RESTful API文档
  • 下一篇讲解 SpringSecurity+JWT 保护你的API(近期推出)
  • 源码上传Git 渴望你的star
  • 本篇博客撰写人: XiaoJinZi 转载请注明出处
  • 学生能力有限 附上邮箱: 986209501@qq.com 不足以及误处请大佬指责
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342