Spring Boot從零入門5_五臟俱全的RESTful Web Service構建

本文屬於原創,轉載註明出處,歡迎關注微信小程序小白AI博客 微信公衆號小白AI或者網站 xiaobaiai.nethtml

[TOC]java

1 前言

這一節咱們正式進入Spring Boot的WEB服務開發,在WEB服務中,不可缺乏的咱們須要去提供API出來,那麼就少不了設計API,而當前流行的一套API設計風格就是REST API ,接下來咱們會介紹什麼是RESTful API以及它的特色和如何去設計。完成設計後,咱們會使用Spring Boot + MVC架構去實現一個RESTful Web Service。本文的全部內容都是通過多方面考察和參考官方資料,本着嚴謹的態度爲本身也爲一塊兒學習的同窗們負責,由淺入深,層層展開,讓本身有不同的收穫。一塊兒加油吧!git

2 名詞術語

名詞術語 釋義
RESTful RESTFUL是一種網絡應用程序的設計風格和開發方式,是目前流行的 API 設計規範,用於 Web 數據接口的設計。經過使用事先定義好的接口與不一樣的服務聯繫起來,瀏覽器使用POST,DELETE,PUT和GET四種主要請求方式分別對指定的URL資源進行增刪改查操做。所以,RESTful是經過URI實現對資源的管理及訪問,具備擴展性強、結構清晰的特色。

3 一分鐘瞭解RESTful API

RESTful 是目前流行的 API 設計規範,用於 Web 數據接口的設計。web

RESTful 對 URL 或者 API 的設計總的原則就是將全部操做對象都看做一個資源,操做這個(些)資源(名詞)的方法經過 HTTP的方法類型(動詞)去實現:spring

# GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
GET:讀取(Read)資源信息
POST:新建(Create)資源
PUT:更新(Update)資源
PATCH:更新(Update)資源,一般是部分更新
DELETE:刪除(Delete)資源
複製代碼

經過對於上述概念的理解,咱們舉一些經常使用示例來判斷設計是否符合RESTful規範。json

POST /api/v1/users/login             # 否,具體分析見後面
POST /api/v1/users                   # 是,建立一個新用戶
GET  /api/v1/users/:username         # 是,獲取全部用戶信息或者指定用戶名的信息
DELETE /api/v1/users/:username       # 是,刪除全部用戶或者刪除指定用戶
GET /api/v1/getUserInfo              # 否,自己就是利用HTTP的方法作動詞,無需另外添加
複製代碼

更多的 RESTful API 示例能夠參考主流網站的開發API,如碼雲(gitee.com/api/v5/swag…)小程序

特例講述:用戶登陸和登出的 RESTful API 怎麼設計呢?微信小程序

  1. 登陸(login)和登出(logout)是兩個動做,自己也是兩個動詞,所以從表面上看咱們是沒法設計成RESTful API的
    • POST /api/v1/users/login body參數爲username和password,這樣既不安全也不是RESTful API
  2. 登陸和登出本質上就是獲取一個具備時間限定的會話(session),其中保持這個會話的樞紐就是token,而REST中是沒有session的,RESTful架構中的原則就是無狀態,自己的釋義就是狀態轉移。
  3. 實際在 API 上還有經過 OAuth 來實現受權操做

由於,這裏的結論就是登陸登出僅做爲URL設計,並不做爲RESTful API設計。api

注意:一些HTTP方法,例如HEAD,GET,OPTIONS和TRACE被定義爲安全的,這意味着它們僅用於信息獲取,而沒有更改服務器的狀態。而POST、PUT、DELETE就不是定義爲安全的,由於會更新信息狀態。 關於無狀態:無狀態意味着每一個HTTP請求都是徹底隔離的。客戶端發出HTTP請求時,它包含服務器完成該請求所需的全部信息。服務器從不依賴先前請求中的信息。若是該信息很重要,則客戶端將不得不在後續請求中再次發送該信息。無狀態也帶來了新功能。在負載平衡的服務器之間分發無狀態應用程序更加容易。無狀態應用程序也易於緩存。瀏覽器

4 MVC/Model 2

在MVC/Model 2 中, 將Web 應用劃分爲模型、視圖與控制器三個部分:

  • 控制器(Controller)的職責,橋樑
    • 接受請求
    • 驗證請求
    • 判斷要轉發請求給哪一個模型
    • 判斷要轉發請求給哪一個視圖
  • 模型(Model)的職責
    • 保存應用程式狀態
    • 執行應用程序業務邏輯(Business logic)
  • 視圖(View)的職責
    • 提取模型狀態
    • 執行呈現迴應畫面

下圖框架是Model2的結構。MVC框架有兩個版本,一個是Model1,也就是MVC的第一個版本,它的視圖中存在着大量的流程控制和代碼開發,也就是控制器和視圖還具備部分的耦合。

MVC/Model2跟我這一篇的講述有什麼關聯呢?由於使用Spring Boot構建WEB應用依賴的就是spring-boot-starter-web,而這個依賴項裏就是使用的spring-webmvc,採用MVC結構。接下來咱們就講述如何去建立WEB服務。分兩部分來說述,一部分就是隻有控制器和視圖的簡單RESTful WEB Service,另外一部分利用@Service Spring Boot應用中完整的呈現MVC結構。

5 簡單 RESTful Web Service 構建

5.1 功能和API設計

咱們實現的功能就是對用戶實現簡單的管理,如查詢,新增,刪除,更新操做。設計的API以下:

# 獲取全部用戶信息
GET /api/v1/users
# 新增一個用戶
POST /api/v1/users
# 刪除指定用戶
DELETE /api/v1/users/{id}
# 更新指定用戶信息
PUT /api/v1/users/{id}
複製代碼

5.2 項目實現

一樣地,咱們創建一個Spring Starter Project項目,將Spring Boot Starter Web依賴項添加到構建配置文件pom.xml(使用Marven構建)中:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製代碼

添加用戶屬性類 User.java :

package com.xiaobaiai;

public class User {
	private String id;
	private String name;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
複製代碼

添加控制器類 UserServiceController.java :

package com.xiaobaiai;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1")
public class UserServiceController {
	private static Map<String, User> userRepo = new HashMap<>();
	static {
		User ethan = new User();
		ethan.setId("1");
		ethan.setName("Ethan");
		userRepo.put(ethan.getId(), ethan);

		User xiaoming = new User();
		xiaoming.setId("2");
		xiaoming.setName("Xiaoming");
		userRepo.put(xiaoming.getId(), xiaoming);
	}

	@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
	public ResponseEntity<Object> delete(@PathVariable("id") String id) {
		userRepo.remove(id);
		return new ResponseEntity<>("User is deleted successsfully", HttpStatus.OK);
	}

	@RequestMapping(value = "/users/{id}", method = RequestMethod.PUT)
	public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody User user) {
		userRepo.remove(id);
		user.setId(id);
		userRepo.put(id, user);
		return new ResponseEntity<>("User is updated successsfully", HttpStatus.OK);
	}

	@RequestMapping(value = "/users", method = RequestMethod.POST)
	public ResponseEntity<Object> createProduct(@RequestBody User user) {
		userRepo.put(user.getId(), user);
		return new ResponseEntity<>("User is created successfully", HttpStatus.CREATED);
	}

	@GetMapping(value = "/users")
	public ResponseEntity<Object> getProduct() {
		return new ResponseEntity<>(userRepo.values(), HttpStatus.OK);
	}
}
複製代碼

添加應用類 Test05HelloworldApplication.java

package com.xiaobaiai;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Test05HelloworldApplication {

	public static void main(String[] args) {
		SpringApplication.run(Test05HelloworldApplication.class, args);
	}

}
複製代碼

5.3 編譯測試

編譯啓動,應用實際 RESTful API 有:

# 獲取全部用戶信息
GET http://localhost:8080/api/v1/users
# 新增一個用戶,參數經過body傳遞
POST http://localhost:8080/api/v1/users
# 更新一個用戶信息
PUT http://localhost:8080/api/v1/users/{id}
# 刪除指定用戶
DELETE http://localhost:8080/api/v1/users/{id}
複製代碼

利用POSTMAN能夠測試接口的功能運轉:

5.4 代碼分析

在《Spring Boot從零入門3_建立Hello World及項目剖析》咱們就分析過代碼,咱們這裏仍是回顧下,咱們知道 @SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration@ComponentScan掃描全部類Component,如Controller,Service,Repository。而@EnableAutoConfiguration將自動解析視圖(views),視圖解析器(view resolvers)等。@RestController是Spring4以後加入的註解,原來在@Controller中返回json須要@ResponseBody來配合,若是直接用@RestController替代@Controller就不須要再配置@ResponseBody,默認返回json格式。@RequestMapping用來配置url映射,如今更多的也會直接用以Http Method直接關聯的映射註解來定義,好比:GetMappingPostMappingDeleteMappingPutMapping等,@RequestMapping能夠映射到整個類或特定的處理方法上,一般,類級別的註解將特定的請求路徑(或路徑模式)映射到表單控制器上,其餘方法級別的註解做用特定的HTTP請求方法。

在控制器代碼裏,經過@RequestMapping/api/v1映射到整個User控制器上,User控制器裏具體的方法則由@RequestMapping做用到指定的HTTP請求方法上,即用戶的增刪查改。具體的@RequestMapping參數設置就不展開了,後面博文會專門講述經常使用註解的做用和使用。

6 五臟俱全的 RESTful WEB Service 構建

6.1 工程實現

上面的簡單RESTful WEB Service構建,直接經過Controller去訪問和操做數據的,對於MVC結構,顯然缺乏了Model專門來處理數據,對業務的邏輯處理隔離度也不夠,簡單的WEB服務需求缺乏Model也是能夠的,畢竟這樣設計不是很複雜,不過業務大了,咱們須要儘可能下降業務邏輯與上層視圖的耦合度,增長模塊的可重用性。下面咱們來實現MVC結構。

首先咱們建立一個業務操做接口 UserService.java 包括對用戶的增刪查改 :

package com.xiaobaiai;

import java.util.Collection;

public interface UserService {
	public abstract void createUser(User user);
	public abstract void updateUser(String id, User user);
	public abstract void deleteUser(String id);
	public abstract Collection<User> getUsers();
}
複製代碼

經過@Service建立一個組件,用於在與@RestController類文件分開的不一樣層中編寫業務邏輯,即對用戶的增刪查改的業務邏輯實現:

package com.xiaobaiai;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
	private static Map<String, User> userRepo = new HashMap<>();
	static {
		User ethan = new User();
		ethan.setId("1");
		ethan.setName("Ethan");
		userRepo.put(ethan.getId(), ethan);

		User xiaoming = new User();
		xiaoming.setId("2");
		xiaoming.setName("Xiaoming");
		userRepo.put(xiaoming.getId(), xiaoming);
	}
	
	@Override
	public void createUser(User user) {
		// TODO Auto-generated method stub
		userRepo.put(user.getId(), user);
	}

	@Override
	public void updateUser(String id, User user) {
		// TODO Auto-generated method stub
		userRepo.remove(id);
		user.setId(id);
		userRepo.put(id, user);
	}

	@Override
	public void deleteUser(String id) {
		// TODO Auto-generated method stub
		userRepo.remove(id);
	}

	@Override
	public Collection<User> getUsers() {
		// TODO Auto-generated method stub
		return userRepo.values();
	}
}
複製代碼

最後在控制器類裏使用@Autowired將Service接口與實現組裝起來。

package com.xiaobaiai;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1")
public class UserServiceController {
	@Autowired
	UserService userService;

	@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
	public ResponseEntity<Object> delete(@PathVariable("id") String id) {
		userService.deleteUser(id);
		return new ResponseEntity<>("User is deleted successsfully", HttpStatus.OK);
	}

	@RequestMapping(value = "/users/{id}", method = RequestMethod.PUT)
	public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody User user) {
		userService.updateUser(id, user);
		return new ResponseEntity<>("User is updated successsfully", HttpStatus.OK);
	}

	@RequestMapping(value = "/users", method = RequestMethod.POST)
	public ResponseEntity<Object> createProduct(@RequestBody User user) {
		userService.createUser(user);
		return new ResponseEntity<>("User is created successfully", HttpStatus.CREATED);
	}

	@GetMapping(value = "/users")
	public ResponseEntity<Object> getProduct() {
		return new ResponseEntity<>(userService.getUsers(), HttpStatus.OK);
	}
}

複製代碼

6.2 代碼分析

相對只有控制器的WEB服務,這裏加入了@Service實現的Model層,而UserService接口的實體是經過@Autowired鏈接起來的,即:

@Autowired
UserService userService;
複製代碼

固然咱們還有一種主流寫法就是經過構造函數的形式,最後達到的效果是同樣的:

UserService userService;;

@Autowired
public UserServiceController(UserService userService) {
    this.userService = userService;
}
複製代碼

若是UserService有多個實體類,這個時候@Autowired如何去綁定實體類的呢?

這個時候須要用到@Qualifier來指定實體類的名稱:

@Service
// 指定UserServiceImpl名稱爲a
@Qualifier("a")
public class UserServiceImpl implements UserService { }
複製代碼
@Service
@Qualifier("b")
public class UserServiceImpl1_1 implements UserService { }
複製代碼

最後在控制器裏自動組裝的時候指定具體的實體類名稱就能夠了:

// 寫法1
@Autowired
@Qualifier("a")
UserService userService;

// 寫法2
UserService userService;

@Autowired
public UserServiceController(@Qualifier("a") UserService userService) {
    this.userService = userService;
}
複製代碼

6.3 延伸

@Resource也可以實現自動裝配Bean的功能,那@Autowired@Resource有什麼區別呢?

  • @Autowired與@Resource均可以用來裝配bean. 均可以寫在字段上,或寫在setter方法上
  • @Autowired默認按類型裝配,是spring支持的註解,默認狀況下必需要求依賴實體類必須存在,若是要容許null值,能夠設置它的required屬性爲false
  • 想使用名稱裝配能夠結合@Qualifier註解進行使用
  • @Resource 是JDK1.6支持的註解,默認按照名稱進行裝配,名稱能夠經過name屬性進行指定,若是沒有指定name屬性,當註解寫在字段上時,默認取字段名,按照名稱查找,若是註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。若是name屬性一旦指定,就只會按照名稱進行裝配

有了比較完善的控制器和Model,那麼對於View層有什麼專用模板引擎嗎?

Thymeleaf是基於Java用於建立Web應用程序的的模板引擎。它爲在Web應用程序中提供XHTML / HTML5提供了良好的支持。相似的還有Apache FreeMarkerMustacheGroovy Templates。後面博文會詳解介紹。

7 總結

經過對RESTful API的介紹以及結合實例工程,咱們基本瞭解了一個RESTful WEB ServiceSpring Boot框架下是怎麼實現的。經過對工程代碼的分析,讓咱們對@Autowired@ResourceQualifier等註解也有了實質瞭解。感受如今正式步入JAVA WEB的開發。接下來,繼續!

8 參考資料

相關文章
相關標籤/搜索