最近在弄監控主機項目,對javaweb又再努力學習。實際的項目場景中,前后分離幾乎是所以項目的標配,全棧的時代的逐漸遠去,后端負責業務邏輯處理,前端負責數據展示成了一種固定的開發模式。像thymeleaf這種東西沒法實現前后端分離模板難學也只有寫java的才用吧,還是用js模板引擎接受json好。
1. Json報文
SpringBoot 默認會使用 Json 作為響應報文格式。首先,我們創建一個 UserController 用於處理前端的 Web 請求。
定義一個簡單的控制器,與通常返回 Url 的 Controller 不一樣的是,login() 使用了 @ResponseBody 注解,它表示此接口響應為純數據,不帶任何界面展示,可以獲得標准Json。
@Controller //等同於springmvc @Controller 可以返回字符串 跳轉至jsp等頁面 @RequestMapping("/user") public class UserController { @RequestMapping("/login") @ResponseBody public RespEntity login(@RequestBody ReqUser reqUser) { //使用reqUser模型來接受,而不用User,實現 接收數據和實體的解耦和 User user = new User(); if(reqUser != null) { user.setName(reqUser.getName()); user.setPassword(reqUser.getPassword()); } return new RespEntity(RespCode.SUCCESS, user); //返回的響應實體具體看下節 } }
【注】:
使用@Controller 注解,在對應的方法上,視圖解析器可以解析return 的jsp,html頁面,並且跳轉到相應頁面
若返回json等內容到頁面,則需要加@ResponseBody注解
對於上面的代碼來說,還可以做進一步的優化,由於所有的 Restful 接口都只是返回數據,所以我們可以直接在類級別上添加 @ResponseBody 注解。而大多數情況下,@Controller 與 @ResponseBody 又會一起使用,所以我們使用 @RestController 注解來替換掉它們,從而更加簡潔地實現功能。
2. 接口規范
對於每一家公司來說,都會定義自己的數據規范,一個統一且標准的數據規范對於系統維護來說是非常重要的,也在很在程度上提升了開發效率。
2.1 響應報文規范
接口響應至少需要告訴使用方三項信息:狀態碼、描述、數據。其中,數據不是每個接口必須的,如果只是一個簡單修改的動作,可能就沒有必須返回數據了。下面我們定義一個 RespEntity類來封裝我們的響應報文model:
public class RespEntity { private int code; private String msg; private Object data; public RespEntity(RespCode respCode) { this.code = respCode.getCode(); this.msg = respCode.getMsg(); } public RespEntity(RespCode respCode, Object data) { this(respCode); this.data = data; } ... }
同時,定義一個枚舉類來維護我們的狀態碼:
public enum RespCode { SUCCESS(0, "請求成功"), WARN(-1, "網絡異常,請稍后重試"); private int code; private String msg; RespCode(int code, String msg) { this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }
這樣,我們的響應數據規范已基本建立。
2.2 請求數據規范
響應報文格式我們已經定義好了,那么請求數據我們如何接收呢?
一般來說,請求與響應會使用相同的報文形式。如果響應為Json,那么請求也建議使用Json。
為登錄請求添加輸入參數,首先,需要我們定義好用戶實體User類,直接在映射方法login() 使用該實體進行參數接收,並將接收到的參數直接返回,1.節代碼已實現。
調出Postman,(參考 postman 入門使用:https://www.cnblogs.com/yelao/p/9836252.html)
填寫正確的Url,選擇POST方式發送請求,選擇Body,將 Content-Type 設置成 application/json,填入 Json 格式的請求數據,點擊 Send 即可得到如下結果。
數據接收非常成功,但在上面的響應報文中,存在着了一個非常嚴重的問題,那就是用戶的密碼也隨同用戶信息一起返回給了客戶端,顯然這並不是一種正確的做法。
我們需要對其進行一次過濾,由於 SpringBoot 默認使用 Jackson 作為 Json 序列化工具,如果想要過濾掉響應中的某些字段,只需在過濾字段對應的 get 方法上加上 @JsonIgnore 注解即可。
但這樣又會引發另外一個問題,那就是請求中的字段也被過濾掉了,對於這種問題,可以采用抽離請求參數模型的方式進行處理,即自定義一套參數接收的 Model,比如,接收用戶登錄的會使用 ReqUser 來進行參數接收,這樣使得請求參數模型與數據庫映射實體完全分離,在一定程度上提升了系統的安全性。替換成 Model 對象后(1.節的代碼已經替換好了),我們就可以在數據庫映射實體 User 上增加 @JsonIgnore 注解忽略該字段的序列化,而不影響請求參數的輸入。
3. 參數校驗
出於系統健壯性的考慮,我們需要對所有的參數進行必要性校驗,如:登錄請求時,如果沒有用戶名,程序應該立即駁回該請求。上面請求參數模型(Model)的抽象也使得我們對數據校驗更加方便,當然主要還是依賴於 SpringBoot 的 Validate 功能的強大支持。
3.1. 簡單參數校驗
對於登錄接口來說,用戶名與密碼都是必輸的,那么我們現在為其添加上對應的參數校驗,無需 if-else 判斷,簡單的幾個注解就可以幫助我們完成所有的工作。
public class LoginController { @RequestMapping("/login") @ResponseBody public RespEntity login(@RequestBody @Valid ReqUser reqUser) { } } ---- public class ReqUser { @NotBlank(message = "用戶名不能為空") public String getName() { return name; } @NotBlank(message = "密碼不能為空") public String getPassword() { return password; } ... }
我們為請求參數的 Model 對象ReqUser 加上了 @Valid 注解,並在 Model 類中對需要校驗字段的 get 方法上添加相應的校驗注解。效果如下:
3.2. 復雜參數校驗
- 正則表達式校驗
如果用戶的登錄名為手機號,那么就需要對登錄名的格式做進一步的校驗,下面使用正則表達式來校驗手機號的合法性。
@NotBlank(message = "用戶名不能為空") @Pattern( regexp = "1(([38]\\d)|(5[^4&&\\d])|(4[579])|(7[0135678]))\\d{8}", message = "手機號格式不合法" ) public String getUsername() { return username; }
- 自定義校驗注解
在系統使用過程中,有很多地方需要對手機號的格式進行校驗,如:注冊、驗證碼發送等。
但校驗手機號的正則表達式又過於復雜,如果多處編寫,一旦運營商增加某個號段,對程序的維護人員來說就是一個噩耗。這時,可以使用自定義校驗注解來代替這些常用的校驗。
手機號校驗注解 Phone:
@Constraint(validatedBy = PhoneValidator.class) @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Phone { String message() default "手機號格式不合法"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
手機號校驗實現類 PhoneValidator:
public class PhoneValidator implements ConstraintValidator<Phone, String> { private Pattern pattern = Pattern.compile("1(([38]\\d)|(5[^4&&\\d])|(4[579])|(7[0135678]))\\d{8}"); @Override public void initialize(Phone phone) { } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { return pattern.matcher(value).matches(); } }
Model 上的使用:
@Phone public String getUsername() { return username; }
這樣的話,如果因為某些不可抗拒因素導致校驗規則的變動,只需要修改一處理即可,維護成本大大降低。
4. Xml 報文
大多數情況下,使用 Json 就可以滿足我們的需求了,但仍然存在某些特定的場景需要使用到 XML 形式的報文,如:微信公眾號開發。不過不用擔心,切換成 XML 報文也只需要做輕微的改動,添加相關依賴如下:"com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.8.8"
然后就可以開始進行測試了,此處借助一個模擬 HTTP 請求工具(Postman)來協助我們測試該接口:
在上面的測試范例里,我們指定了 Accept 為 text/xml,這樣 SpringBoot 就會返回 XML 形式的數據。