SpringBoot编程基础教程:RESTful API开发

发布于:2023-10-25 ⋅ 阅读:(84) ⋅ 点赞:(0)

作者:禅与计算机程序设计艺术

1.背景介绍

概述

近年来,基于云计算、物联网、大数据、人工智能等新兴技术的驱动下,互联网应用逐渐转向了以服务端为中心的架构模式。目前,越来越多的互联网公司开始采用面向服务的架构设计模式,即将复杂且庞大的业务系统通过服务化拆分成独立部署的微服务,并通过API Gateway统一暴露,为第三方客户端提供访问接入点。

在这种架构模式下,开发人员需要编写健壮可靠的服务端应用程序,实现业务逻辑的持久化、数据缓存、异步处理等功能。在Java语言中,Spring Boot框架是构建快速、可靠、生产级的基于Spring的微服务应用程序的不二之选。

本教程通过“SpringBoot编程基础教程:RESTful API开发”系列教程,给学习者以全新的视角,从零开始,轻松掌握SpringBoot RESTful API的开发技巧。以下会详细阐述SpringBoot RESTful API开发的相关技术要点,带领大家亲自动手实践,完成一个完整的RESTful风格的服务端开发项目。

知识要求

基本的Java开发能力,包括Java语法、类/对象、集合类、异常处理、IO流、多线程、反射机制、网络编程、数据库操作、正则表达式、设计模式等;熟悉HTTP协议、Restful规范。

2.核心概念与联系

RESTful API(Representational State Transfer)是一种基于HTTP的应用层协议,定义了客户端-服务器之间交换数据的标准接口,旨在提升互联网软件在各个层次的可伸缩性。它将互联网从静态页面到动态资源的变化,从基于表单的Web应用到分布式的API-first系统,都从RESTful的角度重新审视了Web服务的设计方式。

2.1 URI

URI(Uniform Resource Identifier),即统一资源标识符,它是一种抽象的用来标识互联网资源名称的字符串,它可以使得互联网上各种信息资源的位置唯一且可读。RESTful API一般以名词表示资源类型,比如:

GET /users:获取用户列表 POST /users:创建新用户 DELETE /users/{id}:删除指定ID的用户 PUT /users/{id}:更新指定ID的用户

2.2 HTTP方法

HTTP方法(Hypertext Transfer Protocol Method)是指客户端向服务器发送请求的方法。RESTful API一般以动词或名词表示操作类型,用不同的方法对同一资源执行不同的操作,常用的HTTP方法包括:

GET:获取资源,对应查询操作 POST:新建资源(实体),对应创建操作 PUT:更新资源(实体),对应修改操作 PATCH:更新资源(局部),对应部分修改操作 DELETE:删除资源,对应删除操作

2.3 请求参数

请求参数(Request Parameter)是指客户端向服务器传递的数据,例如查询条件、提交表单数据、上传文件等。请求参数通过URL或者JSON格式发送给服务器。

2.4 返回结果

返回结果(Response Result)是指服务器返回给客户端的数据,例如查询结果、文件下载、JSON格式数据等。返回结果通常是JSON格式。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

为了帮助大家理解RESTful API的开发过程,本节将介绍RESTful API开发的一些基本概念和流程。

3.1 启动类

Spring Boot应用一般都是使用主类作为启动入口。其中,@SpringBootApplication注解可以标注启动类,其作用相当于将启动类所在包及子包下的所有类扫描到Spring容器中,方便IoC依赖注入。

package com.example.demo;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring Boot启动运行
    }

}

3.2 配置类

配置类(Configuration Class)是Spring Boot提供的用于配置Spring Bean的注解。在配置类上加入@Configuration注解,表明它是一个配置类,然后在该类里声明Bean,即可在其他Bean中使用。

package com.example.demo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan("com.example.demo")//注册当前包及子包中的组件
@EnableWebMvc//启用MVC支持
public class AppConfig {

}

3.3 控制器(Controller)

控制器(Controller)是 Spring MVC 中的一个组件,负责接收客户端的请求,并按照指定的规则生成相应的响应。可以通过@RestController注解来定义一个控制器类,并在类中声明各个请求的映射方法。

package com.example.demo;

import org.springframework.web.bind.annotation.*;

@RestController
public class HelloWorldController {

    @RequestMapping("/")
    public String hello() {
        return "Hello World!";
    }

}

3.4 服务(Service)

服务(Service)是用于业务逻辑处理的类,一般以业务领域的名词命名,比如UserService。在业务层中,我们可以定义各种操作数据库的方法,然后使用@Autowired注解注入Repository(DAO层)或者其他服务。

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public List<User> getUsers() {
        return userDao.findAll();
    }

   ...

}

3.5 DAO(Data Access Object)

DAO(Data Access Object)是用于访问数据库的类,一般以数据源+表名命名。在DAO层中,我们可以使用Spring Data JPA(Java Persistence API)进行ORM映射。

package com.example.demo.dao;

import org.springframework.data.jpa.repository.JpaRepository;

interface UserDao extends JpaRepository<User, Long> {}

3.6 ORM映射

ORM(Object Relational Mapping,对象关系映射)是一种编程范式,它将关系型数据库的一组表结构映射到对象的形式,以方便开发人员操作数据库。在Spring Boot中,我们可以使用Spring Data JPA(Java Persistence API)来实现ORM映射。

3.7 Jackson序列化

Jackson(Java Automatic Serialization Library)是一个Java开发的高性能的序列化库,其提供了简洁灵活的API,能够将Java对象转换为JSON格式或XML格式,也可以将JSON或XML格式的字符串还原为Java对象。

package com.example.demo.vo;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.*;
import java.time.LocalDateTime;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)//JSON键值驼峰策略
public class UserVo {

    private Long id;
    private String username;
    private Integer age;
    private LocalDateTime createTime;

}

4.具体代码实例和详细解释说明

4.1 创建用户

创建一个Controller类,通过@PostMapping("/users")注解定义添加用户的接口。并定义一个User对象,绑定到参数上。最后调用UserService中的addUser方法保存用户到数据库。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping("/api/v1/")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/users")
    public ResponseEntity create(@RequestBody @Valid User user){
        log.info("create user: {}",user);
        try{
            userService.save(user);
        }catch (Exception e){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.ok().build();
    }

}

4.2 更新用户

创建一个Controller类,通过@PutMapping("/users/{id}")注解定义更新指定ID的用户的接口。并定义一个User对象,绑定到参数上。最后调用UserService中的updateUser方法根据ID更新用户。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping("/api/v1/")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @PutMapping("/users/{id}")
    public ResponseEntity update(@PathVariable Long id,@RequestBody @Valid User user){
        log.info("update user by id: {}, user:{}",id,user);
        Optional<User> optional = userService.findById(id);
        if(!optional.isPresent()){
            throw new ResourceNotFoundException("user","id",id);
        }
        try{
            userService.save(user);
        }catch (Exception e){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.noContent().build();
    }

}

4.3 删除用户

创建一个Controller类,通过@DeleteMapping("/users/{id}")注解定义删除指定ID的用户的接口。并调用UserService中的deleteUser方法根据ID删除用户。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @DeleteMapping("/users/{id}")
    public ResponseEntity delete(@PathVariable Long id){
        log.info("delete user by id: {}",id);
        Optional<User> optional = userService.findById(id);
        if(!optional.isPresent()){
            throw new ResourceNotFoundException("user","id",id);
        }
        try{
            userService.deleteById(id);
        }catch (Exception e){
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.ok().build();
    }

}

4.4 查询用户列表

创建一个Controller类,通过@GetMapping("/users")注解定义查询用户列表的接口。并调用UserService中的getUsers方法查询所有的用户。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public ResponseEntity getAll(){
        log.info("get all users");
        List<User> users = userService.getUsers();
        return ResponseEntity.ok(users);
    }

}

4.5 模糊查询用户列表

创建一个Controller类,通过@GetMapping("/users/search")注解定义模糊查询用户列表的接口。并定义一个Query对象,包含搜索关键词、页码、每页记录数。然后调用UserService中的search方法模糊查询用户列表。

package com.example.demo.controller;

import com.example.demo.domain.query.UserSearchQuery;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1/")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users/search")
    public ResponseEntity search(@ModelAttribute UserSearchQuery query){
        log.info("search user with keyword={}, page={}",query.getKeyword(),query.getPage());
        Pageable pageable = PageRequest.of(query.getPage()-1,query.getSize(), Sort.Direction.ASC,"username");
        Page<User> page = userService.searchByKeyword(pageable,query.getKeyword());
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/v1/users/search");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

}

4.6 数据分页显示

创建一个工具类,实现分页相关的功能,比如生成分页链接Header,解析分页查询条件,计算分页信息等。

package com.example.demo.utils;

import org.springframework.data.domain.*;
import org.springframework.http.HttpHeaders;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;

public final class PaginationUtil {

    /**
     * 生成分页链接header
     */
    public static HttpHeaders generatePaginationHttpHeaders(Page<?> page, String url){
        HttpHeaders headers = new HttpHeaders();
        int totalPages = page.getTotalPages();

        long limit = page.getSize()+1L;//加1是因为超过总数量的记录也算做一页

        for (long i=0L ;i <totalPages ;i++){

            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
            MultiValueMap<String, String> params = builder.queryParams().toMultiValueMap();

            //修改页码的参数
            params.set("page",Long.toString(i));

            //判断是否已经到达最后一页
            boolean isLastPage = false;
            if((limit*i + page.getSize()) >= page.getTotalElements()){
                isLastPage = true;
            }

            //如果已到达最后一页,则不再增加page参数,防止后续参数丢失
            if (!isLastPage) {

                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < params.size(); ++j) {
                    sb.append(params.keySet().toArray()[j]).append('=').append(params.values().toArray()[j][0]);
                    if (j!= params.size() - 1) {
                        sb.append('&');
                    }
                }
                uri = URLDecoder.decode(sb.toString());
            }else{
                uri = "";
            }

            headers.add("link", "<" + uri + ">; rel=\"self\", ");
        }

        return headers;
    }

}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到