天天看點

Spring Boot 的 RESTful API 設計與實作

作者:測試者穆勒

RESTful 是一種規範,符合 RESTful 的 Api 就是 RESTful Api。簡單的說就是可聯網裝置利用 HTTP 協定通過 GET、POST、DELETE、PUT、PATCH 來操作具有 URI 辨別的伺服器資源,傳回統一格式的資源資訊,包括 JSON、XML、CSV、ProtoBuf、其他格式。

RESTful 的核心思想是,用戶端發出的資料操作指令都是"動詞 + 賓語"的結構。比如,GET /case 這個指令,GET 是動詞,/case 是賓語。

RESTful API簡介

  • RESTful 架構遵循統一接口原則,不論什麼樣的資源,都是通過使用相同的接口進行資源的通路。接口應該使用标準的 HTTP 方法如 GET ,PUT 和 POST ,并遵循這些方法的語義。

設計規範

常用的動詞有以下 5 個

Spring Boot 的 RESTful API 設計與實作
Spring Boot 的 RESTful API 設計與實作

詳情見 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

Spring Boot 實作 RESTful API

我們可以通過 Spring Boot 注解來實作 RESTful API 。

現在需要編寫的是對一個使用者的增删改查操作,如下表是一個非 RESTful 和 标準 RESTful 的對比表。

Spring Boot 的 RESTful API 設計與實作

下面我們着重介紹下以下兩對注解。

Controller 一般應用在有傳回界面的應用場景下。例如,管理背景使用了模闆技術如 thymeleaf 開發,需要從背景直接傳回 Model 對象到前台,那麼這時候就需要使用 Controller 來注解。

RestController 一般應用在隻有接口的應用場景下. 例如開發前後端分離的項目時,通過 Ajax 請求服務端接口,那麼接口就使用 RestController 統一注解。

需要注意的是 RestController 是 Controller 的子集。RestController 是 Spring4 後新加的注解,從 RestController 注解源碼可以看出 RestController 是 Controller 和 ResponseBody 兩個注解的結合體,即Controller=RestController+ResponseBody。

RestController 注解源碼

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

           

RequestMapping 和GetMapping/PostMapping/PutMapping/DeleteMapping 作用一樣,其實可以互相替換,後者是前者的簡化版本。

GetMapping 其實就等于将 RequestMapping 注解的 method 屬性設定為 GET,PostMapping 其實就等于将 RequestMapping 注解的 method 屬性設定為 POST,PutMapping、DeleteMapping 其實就等于将 RequestMapping 注解的 method 屬性分别設定為 PUT、DELETE。

也就是說GetMapping、PostMapping、PutMapping、DeleteMapping 是 RequestMapping 的子集。

我們來看看 RequestMapping 的源碼:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";

    //請求URI
    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {};
    //請求類型,如 GET、POST、PUT、DELETE 等
    RequestMethod[] method() default {};
    //請求參數中必須包含某些參數值,才讓該方法處理。
    String[] params() default {};
    //請求參數中必須包含某些指定的header值,才能讓該方法處理請求。
    String[] headers() default {};
    //請求的内容類型(Content-Type),例如application/json, text/html;
    String[] consumes() default {};
    //響應的内容類型,僅當 request 請求頭中的( Accept )類型中包含該指定類型才傳回;
    String[] produces() default {};
}

           

示例說明:

Spring Boot 的 RESTful API 設計與實作
Spring Boot 的 RESTful API 設計與實作
  • 新增 2 個檔案:dto/UserDto.java 和 controller/HogwartsTestUserController.java ,其中 UserController 類中包括了對使用者的 4 個操作增删改查。
public class UserDto {

    private String name;
    private String pwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

           
/**
 * RESTful API 風格示例 對資源 user 進行操作
 * 本示例沒有使用資料庫,也沒有使用 service 類來輔助完成,所有操作在本類中完成
 * */
@Api(tags = "霍格沃茲測試學院-使用者管理子產品", hidden = true)
@RestController
@RequestMapping("/api/user")
public class HogwartsTestUserController {

    /**
     * 查詢使用者清單,傳回一個JSON數組
     * */
    @ApiOperation("查詢使用者清單")
    @GetMapping("/users")
    @ResponseStatus(HttpStatus.OK)
    public Object getUsers(){
        List<UserDto> list = getData();
        return list;
    }

    /**
     * 查詢使用者資訊,傳回一個建立的JSON對象
     * */
    @ApiOperation("查詢使用者資訊")
    @GetMapping("/users/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Object getUser(@PathVariable("id") Long id){

        if(Objects.isNull(id)){
            return null;
        }

        List<UserDto> list= getData();
        UserDto userDto = getUserDto(id, list);

        return userDto;
    }

    /**
     * 新增使用者
     * */
    @ApiOperation("新增使用者")
    @PostMapping("/users")
    @ResponseStatus(HttpStatus.CREATED)
    public Object addUser(@RequestBody UserDto user){

        List<UserDto> list= getData();
        list.add(user);//模拟向清單中增加資料
        return user;
    }

    /**
     * 編輯使用者
     * */
    @ApiOperation("編輯使用者")
    @PutMapping("/users/{id}")
    @ResponseStatus(HttpStatus.CREATED)
    public Object editUser(@PathVariable("id") Long id,@RequestBody UserDto user){
        List<UserDto> list = getData();
        for (UserDto userDto:list) {
            if(id.equals(userDto.getId())){
                userDto = user;
                break;
            }
        }

        return user;
    }

    /**
     * 删除使用者
     * */
    @ApiOperation("删除使用者")
    @DeleteMapping("/users/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public Object deleteUser(@PathVariable("id") Long id){
        List<UserDto> list = getData();
        UserDto userDto = getUserDto(id, list);
        return  userDto;
    }

    /**
     * 模拟資料
     * */
    private List<UserDto> getData(){
        List<UserDto> list=new ArrayList<>();

        UserDto userDto = new UserDto();
        userDto.setId(1L);
        userDto.setName("admin");
        userDto.setPwd("admin");
        list.add(userDto);

        userDto = new UserDto();
        userDto.setId(2L);
        userDto.setName("HogwartsTest1");
        userDto.setPwd("HogwartsTest1");
        list.add(userDto);

        userDto = new UserDto();
        userDto.setId(3L);
        userDto.setName("HogwartsTest2");
        userDto.setPwd("HogwartsTest2");
        list.add(userDto);

        userDto = new UserDto();
        userDto.setId(4L);
        userDto.setName("HogwartsTest3");
        userDto.setPwd("HogwartsTest3");
        list.add(userDto);

        return  list;
    }

    /**
     *  模拟根據id查詢清單中的資料
     * @param id
     * @param list
     * @return
     */
    private UserDto getUserDto( Long id, List<UserDto> list) {
        UserDto UserDto = null;
        for (UserDto user : list) {
            if (id.equals(user.getId())) {
                UserDto = user;
                break;
            }
        }
        return UserDto;
    }
}

           

擷取全部資源 擷取所有使用者

GET http://127.0.0.1:8081/api/user/users/

響應參數

[
  {
    "id": 1,
    "name": "admin",
    "pwd": "admin"
  },
  {
    "id": 2,
    "name": "HogwartsTest1",
    "pwd": "HogwartsTest1"
  },
  {
    "id": 3,
    "name": "HogwartsTest2",
    "pwd": "HogwartsTest2"
  },
  {
    "id": 4,
    "name": "HogwartsTest3",
    "pwd": "HogwartsTest3"
  }
]

           

擷取單個資源 擷取使用者

GET http://127.0.0.1:8081/api/user/users/3

新增一個資源 新增一個使用者

POST http://127.0.0.1:8081/api/user/users

請求參數

{
  "id": 4,
  "name": "HogwartsTest5",
  "pwd": "HogwartsTest5"
}

           

編輯更新一個資源

PUT http://127.0.0.1:8081/api/user/users/3

請求參數

{
  "name": "HogwartsTest6",
  "pwd": "HogwartsTest6"
}

           

删除一個資源

DELETE http://127.0.0.1:8081/api/user/users/3

下面介紹一些 Spring Boot 常用配置項,通過這些常用配置項,我們可以修改 Spring Boot 的一些預設配置。

修改服務預設端口:

server:
  port: 8093

           

指定服務名稱:

spring:
  application:
    name: aitest

           

多環境配置

spring:
  profiles:
    active: dev

           
Spring Boot 的 RESTful API 設計與實作

如上圖建立 application-dev.yml、application-test.yml、application-uat.yml、application-prod.yml 四套配置檔案環境,我們在四套配置檔案中将設定服務端口号分别設定為 8091/8092/8093/8094。

然後啟動服務,可以看到服務的端口号會和 application.yml 中激活的環境配置資訊一緻。

[更多技術文章](https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=toutiao×tamp=1662397200&author=Muller)

繼續閱讀