前言
編寫博客分享一下,騷氣的RestFul Api O(∩_∩)O哈哈~
一、Restful
- 使用SpringMVC編寫Restful API
通過上面的圖片我們可以看出,傳統的請求方式與RESTFUL請求方式,存在的不同:
1.用URL描述資源
2.使用HTTP方法描述行爲,使用HTTP狀態碼來表示不同的結果
3.使用json交互數據
4.RESTful 只是一種風格,並不是強制的標準。
二、查詢請求
直接上乾貨:
- 編寫測試用例:
package com.zcw.web.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* @ClassName : UserControllerTest
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-16 15:37
*/
@RunWith(SpringRunner.class)//這個註解是告訴我們大家,是如何來運行。
@SpringBootTest
public class UserControllerTest {
//模擬一個web環境
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setup(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void whenQuerySuccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user")
.contentType(MediaType.APPLICATION_JSON_UTF8))
//希望服務器返回的數據
.andExpect(MockMvcResultMatchers.status().isOk())
//返回的json內容
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3));
}
}
- 編寫服務,使用註解聲明RestfulAPI
package com.zcw.web.controller;
import com.zcw.dto.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : UserController
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-16 16:06
*/
@RestController
public class UserController {
@RequestMapping(value = "/user",method = RequestMethod.GET)
public List<User> query(){
List<User> users = new ArrayList<>();
users.add(new User());
users.add(new User());
users.add(new User());
return users;
}
}
- 創建DTO用來存放輸入與輸出數據:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
package com.zcw.dto;
import lombok.Data;
/**
* @ClassName : User
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-16 16:11
*/
@Data
public class User {
private String username;
private String password;
}
- 測試
- 在RestfulAPI中傳遞參數
package com.zcw.dto;
import lombok.Data;
/**
* @ClassName : UserQueryCondition
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-16 17:31
*/
@Data
public class UserQueryCondition {
private String username;
private int age;
private int ageTo;
private String xxx;
}
test方法:
@Test
public void whenQuerySuccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user")
.param("username","jojo")
.param("age","18")
.param("ageTo","60")
.param("xxx","yyyy")
.param("size","15")
.param("page","3")
.param("sort","age,desc")
.contentType(MediaType.APPLICATION_JSON_UTF8))
//希望服務器返回的數據
.andExpect(MockMvcResultMatchers.status().isOk())
//返回的json內容
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3));
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
三、用戶詳情請求
編寫用戶詳情服務:
- 測試用例:
@Test
public void whenGenInfoSucces() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username")
.value("tom"));
}
-
測試
-
@PathVariable映射url片段到java方法的參數
-
在url聲明中使用正則表達式
只接收數字:
-
@JsonView控制json輸出內容
使用步驟:
1.使用接口來聲明多個視圖
@Data
public class User {
public interface UserSimpleView{};
public interface UserDetailView extends UserSimpleView{};
private String username;
private String password;
}
2.在值對象的get方法上指定視圖
需要把我們的@Data註解去掉。
3.在Controller方法上指定視圖
- 測試
四、用戶創建請求
@RequestBody映射請求體到java方法的參數
@Test
public void whenCreateSuccess() throws Exception {
String content="{\"username\":\"tom\",\"password\":null}";
mockMvc.perform(MockMvcRequestBuilders.post("/user")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"));
}
- controller類:
- 測試:
日期類型參數的處理
一般是時間戳,返回給前端。
- 測試:
@Valid註解和BindingResult驗證請求參數的合法性並處理校驗結果
- 測試
五、修改和刪除請求
- Test
@Test
public void whenCreateSuccess() throws Exception {
Date date = new Date(LocalDateTime.now()
.plusYears(1)
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
);
System.out.println(date.getTime());
String content="{\"id\":\"1\",\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}";
String result = mockMvc.perform(MockMvcRequestBuilders.put("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"))
.andReturn().getResponse().getContentAsString();
System.out.println(result);
}
- 服務
@PutMapping("/{id:\\d+}")
public User update(@Valid @RequestBody User user,BindingResult errors){
if(errors.hasErrors()){
errors.getAllErrors()
.stream()
.forEach(error ->{
FieldError fieldError=(FieldError)error;
String field = fieldError.getField()+error.getDefaultMessage();
System.out.println(field);
}
);
}
user.setId("1");
return user;
}
- 修改我們校驗,提示信息,在實體類裏面定義
- 測試:
運行單元測試方法時報錯:
JDBC連接數據庫時出現的Public Key Retrieval is not allowed錯誤
添加了:
&allowPublicKeyRetrieval=true
url: jdbc:mysql://127.0.0.1:3306/zcw20200509?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
自定義註解進行校驗javabean:
- 方便,效率高
package com.zcw.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
String message();
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default {};
}
package com.zcw.validator;
import com.zcw.web.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @ClassName : MyConstraintValidator
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-17 10:11
*/
public class MyConstraintValidator implements ConstraintValidator<MyConstraint,Object> {
@Autowired
private HelloService helloService;
@Override
public void initialize(MyConstraint myConstraint) {
System.out.println("my validator init");
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
//目的,告訴大家,在校驗器中我們可以編寫,我們任何類的,校驗邏輯
helloService.greeting("tom");
System.out.println(o);
return true;
}
}
- 編寫測試業務類:
package com.zcw.web.service;
public interface HelloService {
String greeting(String name);
}
package com.zcw.web.service.impl;
import com.zcw.web.service.HelloService;
import org.springframework.stereotype.Service;
/**
* @ClassName : HelloServiceImpl
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-17 10:17
*/
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String greeting(String name) {
System.out.println("greeting");
return "hello"+name;
}
}
- 測試:
刪除方法:
- Test
@Test
public void whenDeleteSuccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.delete("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk());
}
- 業務代碼
@DeleteMapping("/{id:\\d+}")
public void delete(@PathVariable String id){
System.out.println(id);
}
- 測試:
六、服務異常處理
springboot中默認的錯誤處理機制:
根據狀態碼進行跳轉,默認跳轉。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
您所訪問的頁面不存在
</body>
</html>
- 測試
自定義異常處理
package com.zcw.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName : ControllerExceptionHandler
* @Description : 控制器的錯誤處理器
* @Author : Zhaocunwei
* @Date: 2020-06-17 11:51
*/
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String,Object> handleUserNotExistException(UserNotExistException ex){
Map<String,Object> result = new HashMap<>();
result.put("id",ex.getId());
result.put("message",ex.getMessage());
return result;
}
}
package com.zcw.exception;
/**
* @ClassName : UserNotExistException
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-06-17 11:46
*/
public class UserNotExistException extends RuntimeException {
private String id;
public UserNotExistException(String id){
super("user not exist");
this.id=id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
- 測試: