(6) Web Starter開發API - Controller

Albert Hg
learning-from-jhipster
14 min readAug 14, 2020

本篇內容的程式碼於 Spring_4_UsingWebStarter

在上一篇文章內已經可以順利的執行一個 Spring Boot 的專案,現在我們要使用 spring-boot-starter-web 這個 Starter 開始建立後端API。

spring-boot-starter-web

Web Starter 的主要目的在於搭建一個 Web 的系統,其中包含:

  • RESTful API
  • Spring MVC (捨棄那些繁瑣的 xml 配置以自動配置取代)
  • 內建預設的 Tomcat 容器 (可以直接執行服務而不需使用其他容器)

為了使用 web starter,因此必須於 pom.xml 內加入相關的 dependency,而因為先前已經繼承過 spring-boot-starter-parent,所以不加上版號資訊也沒有關係:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

同時,在 Web Starter 中就已經有包含幾個 dependency:

  • spring-boot-starter
  • jackson
  • spring-core
  • spring-mvc
  • spring-boot-starter-tomcat

因此在上一篇文章中,一開始加入的 spring-boot-starter 的 dependency 就可以移除。

Spring MVC

雖然 Spring Boot 已經將 Spring MVC 優化為自動配置,不過在開始之前,還是需要對 Spring MVC 具備一些基本的概念。

  • M,Model,對傳遞的數據進行封裝
  • V,View,生成呈現資料相關的頁面給客戶端
  • C,Controller,處理Request並回傳相關頁面或數據給客戶端

在早期開發 Java Web,通常 View 的部分是使用 jsp 來進行頁面的開發,但在前後端分離的時代,View 的部分已經漸漸的被 html、css、javascript 或者三大框架 Angular、React、Vue 取代。

對於後端來說,View 的部分就相對地不著墨許多(不過還是有其他板模可以提供View,例如 Thymeleaf ),更多的注意力就專注於開發API、處理邏輯層面、造訪資料庫等等相關的業務邏輯處理。

  • Controller:API的部分,也就是處理 Request 的部分
  • Service:處理業務邏輯的部分
  • Repository:造訪資料庫,對資料庫進行取得資料等相關動作
  • Model:與資料庫交換資料時對資料的封裝層

在 Spring MVC 的框架中,是可以使用「標註 (Annotation)」的方式配置這些內容,因此接著就來認識這些 Annotation 的使用方式與相關內容吧!

Controller 的建立

Controller 是處理 API 接口的部分,因此有一些 Annotation 來專門標記哪些 class 是作為API接口的類別。

常見的有以下幾種:

  • @Controller:可以透過一些板模(Ex: Thymeleaf)回傳一個頁面,或者組合使用 @ResponseBody 的標記來回傳資料(RESTful API)。
  • @RestController:同等於 @Controller + @ResponseBody 的組合

而若開發 RESTful API 時,必須指定每個 API 的方法與路徑,比較常見的方法有 GET、POST、PUT、DELETE 等 RESTful 的方法。對應的 Annotation 如下:

  • @GetMapping()
  • @PostMapping()
  • @PutMapping()
  • @DeleteMapping()

如果想要自訂或修改 Request URL 的請求路徑,則可以透過 @RequestMapping() 來指定請求的映射路徑。

例如要開發一個取得列表的 RESTful API:

@Controller
@RequestMapping("/api")
public class Controller {
@ResponseBody
@GetMapping("/items")
public List<Object> getItems() { ... };
}

同等於:

@RestController
@RequestMapping("/api")
public class Controller {
@GetMapping("/items")
public List<Object> getItems() { ... };
}

那麼你就可以透過如下 URL 取得 Items 的列表:

http://{ip}:{port}/api/items

API 的傳入參數

當使用 API 時勢必得可以傳入參數,可是傳入參數的方法有好幾種情況,因此接著來繼續介紹比較常用傳入參數的方法有哪些。

1. 將參數放在 URL 後面

[GET] http://.../items?id=12&name=apple

像這種情況,在開發 API 時就可以這樣寫:

@GetMapping("/items")
public void getItems(Long id, String name) {...}

不過在這種情形下,URL 後面不一定要加上參數也可以使用 API,只是在function中所傳入的參數會是null。

如果要使某一項參數為「必要參數」,則可以透過 @RequestParam 標註,指定哪些參數為必要參數:

@GetMapping("/items")
public void getItems(@RequestParam Long id, String name) {...}

如此,若在 URL 中沒有加上指定參數時,則會回傳 400 Bad Request。

2. 將參數放在 URL 裡面

[GET] http://.../items/12/apple

像這種情況,在開發 API 時就可以這樣寫:

@GetMapping("/items/{id}/{name}")
public void getItems(
@PathVariable Long id,
@PathVariable String name
) {...}

這裡就是將變數帶入 URL 的路徑上,並且透過 @PathVariable 指定參數的位置與名稱。

3. 將參數放在 Body 裡面

[GET] http://.../items
[BODY]
{
"id":12,
"name":"apple"
}

如果要將參數放在 Body 中,最常用的方式就是使用 JSON 的格式來傳送資料。像這種情況,在開發 API 時就可以這樣寫:

@GetMapping("/items")
public void getItems(@RequestBody Item item) {
item.getId();
item.getName();
...
}

也就是透過 @RequestBody 來傳入一個固定格式的內容,通常用這種方式是已經在後端將傳入的資料定義好,並以 POJO 的方式定義之。

4. HttpServletRequest 取得參數

也可以使用原本 Servlet 的方法來取得參數,例如:

public void getItems(HttpServletRequest request) {
String param = request.getParameter("param");
String[] values = request.getParameterValues("param");
}

更多使用 HttpServletRequest 的方法可以參考如下網址:

https://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html

API 的回傳結果

大部分呼叫 API,期望就是可以得到資料的回覆,因此接著就來看看有那些比較常見的回傳結果的方法吧!

1. 什麼都不回傳

@GetMapping("/items")
public void getItems(Long id) { ... }

2. 直接設定回傳的資料型別

@GetMapping("/items")
public Integer getItems(Long id) {
Integer result;
...
return result;
}

或者你要回傳一個物件:

@GetMapping("/items")
public Item getItems(@RequestBody Item item) {
item.setName("banana");
return item;
}

3. 使用 ResponseEntity 封裝

這是 Spring 提供的一個對於 Response 的封裝,允許我們設定 HTTP 的 Response Code、Response Header、Response Body。

他有兩種實作方式,一種是 new ResponseEntity 物件:

@GetMapping("/items")
public ResponseEntity<Item> getItems(@RequestBody Item item) {
item.setName("banana");
return new ResponseEntity<>(item, HttpStatus.OK);
}
@GetMapping("/items")
public ResponseEntity<Item> getItems(@RequestBody Item item) {
item.setName("banana");
HttpHeaders headers = new HttpHeaders();
headers.add("total-item-count", "20");
return new ResponseEntity<>(item, headers, HttpStatus.OK);
}

另一種是直接使用 ResponseEntity 的 static 靜態方法:

@GetMapping("/items")
public ResponseEntity<Item> getItems(@RequestBody Item item) {
item.setName("banana");
return ResponseEntity.status(HttpStatus.OK).body(item);
}
@GetMapping("/items")
public ResponseEntity<Item> getItems(@RequestBody Item item) {
item.setName("banana");
return ResponseEntity
.status(HttpStatus.OK)
.header("total-item-count", "20")
.body(item);
}

4. HttpServletResponse 回傳結果

這也是原本 Servlet 在處理 Response 的常用方法,他一樣可以設定 Response 的 Header、Status、Body。例如:

@GetMapping("/items")
public void getItems(
HttpServletResponse response,
@RequestBody Item item
) {
item.setName("banana");
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json;charset=UTF-8");
response.setHeader("total-item-count", "20");
response.getWriter().print(item.toString());
}

更多說明可以參考如下網址:

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/ResponseEntity.htmlhttps://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletResponse.htmlhttps://www.baeldung.com/spring-response-entity

實際練習

你可以在 這裡 找到我已經事先建立好的專案,這裡的資料夾結構會以如下方式建立:

management-system
\---src
\---main
\---java
\---com
\---albert
\---management
| ManagementApplication.java
|
+---controller
| ProductController.java
|
\---dto
ProductDTO.java
  • Controller 的資料夾底下是存放 Request Mapping 類的 java 檔
  • dto 的資料夾底下是存放資料在傳遞時所封裝的 POJO 類的 java 檔

在這裡我定義了一個 ProductDTO.java 以作為「Product資訊」的數據封裝,其包含幾個參數:

private Long id;        // 產品的ID
private String name; // 產品的名稱
private Integer remain; // 產品的剩餘數量

而 ProductController.java 是對於 Product 的 Request Mapping 類別,也就是開發 API 接口的部分。在這部分中,實作了上述 API 參數的輸入以及數據的輸出的一些應用。

如果你將這個專案下載下來後,你就可以在 management-system 資料夾的位置,透過 cmd 執行專案:

$ .\mvnw clean spring-boot:run

測試 API

當我們在開發 API 的時候,我們會需要測試 API 的行為,以方便開發與驗證 API 的正確性。目前測試 API 最常用的工具就是 Postman

例如在這裡所建立的專案中,其中有兩隻API,可以依照圖片中的方式進行測試:

  • [GET] http://localhost:8080/products/list1
  • [POST] http://localhost:8080/products/create

文末

這篇文章一開始先介紹了 Spring MVC 的基本架構,並介紹了一些 Controller 的使用說明,其他例如 Service、Repository 的部分會在之後的文章再繼續說明。

最後我們使用 Postman 來協助開發 API,不過也許你覺得要安裝 Postman 很麻煩,或者覺得還要透過 Postman 來協助開發很討厭。

所以在下一篇文章中會介紹一個開發 API 的輔助神器,Swagger,那麼就接著看下去吧!

--

--

Albert Hg
learning-from-jhipster

I am a programmer but love other things. I am a nobody but keep myself going. I am a person who wishes to reach the heaven but lost the wings.