ServerResponse(服務器統一響應數據格式)

ServerResponse(服務器統一響應數據格式)

前言:

其實嚴格來講,ServerResponse應該歸類到common包中。可是我實在太喜歡這玩意兒了。並且用得也很是頻繁,因此忍不住推薦一下。前端

藉此機會,申明一點,這個系列的類並非都是我原創的,都是我從各個項目中看到的,感受很是贊,一點點攢起來的。固然後面也有我本身寫的一些工具。重要的是學習,從中學習到知識,就算脫離了這些工具,咱們也能夠本身寫一個。java

場景:

這個場景我真的以爲只要寫過接口的,都須要這個。web

其實,在剛剛接觸代碼的時候,看到大佬接口返回的JSON。JSON裏面除了必要的data外,還有各類狀態碼,狀態說明什麼的,感受很厲害。後來漸漸明白了,這個東西是必須的,你不寫試試,看與你交互的大佬會不會把你拍成肉餅。spring

演進:

1.直接返回請求的數據:

後端:呀,前端發來的這個請求,數據庫沒有對應數據啊。返回一個null吧。數據庫

前端:大哥,你返回給我一個null,是否是接口有問題啊?後端

後端:那是你請求的數據在數據庫中沒有。服務器

前端:哦。那我知道了。微信

後端:呀,前端發來的這個請求,參數不對啊(可能必要參數爲空什麼的)。我要返回null。架構

前端:大哥,你給我返回個null,是數據庫沒有對應數據嘛?可是這個條件應該有數據啊。app

後端:不是的,你請求的參數有問題啊。

前端:大哥,那你卻是給我要給回饋啊。不然,我還覺得是你接口沒數據呢。

後端:好的吧。讓我想一想。

2.返回一個對象ResultVo(包含data與code,data爲請求的數據,code爲狀態碼):

後端:嘿,兄弟。我想到了一個好辦法,我寫了一個ResultVo,它是這樣的……%¥&¥……。

前端:好的。我瞭解了。

後端:呀,前端發來的這個請求,沒有足夠的權限啊。我要返回data=null&code=10。而後在常量表中設置一下。

前端:我剛剛無心間發現,你的code又增長了10,什麼意思?

後端:啊。忘了告訴你了。code=10表示權限不足。

前端:那我須要就這個狀況,給用戶提供專門的說明呀。

後端:這樣效率過低了。並且之後可能會有更復雜多變的狀況。我得想一想辦法。

3.返回一個對象ResultVo2(新增msg屬性,充當響應的說明):

後端:嘿,兄弟。我將原來的ResultVo進行了升級,它是這樣的&……%&%&……。

前端:這挺不錯的,之後不少地方,我能夠直接顯示msg就好了。可是,如今有一個問題,如今的code太多了。我每次進行處理時都要遍歷判斷,而我經常只須要判斷這個響應是否成功了。

後端:這樣啊。我還得再改進一下。

4.ServerResponse:

後端:請教大佬後,我獲得了很是棒的解決方案。而且,我根據本身的業務狀況,進行細微的調整,這下就沒什麼問題了。

前端&後端:咱們感覺到了效率的顯著提高,以及最爲重要的代碼規範(契約)。

做用:

ServerResponse就是用來統一服務器接口調用的響應

代碼:

package tech.jarry.learning;
    
    
    import lombok.AllArgsConstructor;
    import lombok.NoArgsConstructor;
    import lombok.RequiredArgsConstructor;
    import org.codehaus.jackson.annotate.JsonIgnore;
    import org.codehaus.jackson.map.annotate.JsonSerialize;
    
    import java.io.Serializable;
    
    /**
     * @Author: jarry
     */
    // 確保序列化JSON時,若是是null對象,其key也會消失。
    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
    // 生成無參構造,確保在RPC調用時,不會出現反序列失敗
    @NoArgsConstructor
    public class ServerResponse<T> implements Serializable {
    
        private int status;
        private String msg;
        private T data;
    
        private ServerResponse(int status) {
            this.status = status;
        }
    
        private ServerResponse(int status, String msg) {
            this.status = status;
            this.msg = msg;
        }
    
        //  這裏存在一個問題,若是構造函數傳入的參數列表爲(int,String),那麼是調用上面的(int,String),仍是這裏的(int,T),畢竟T做爲泛型是能夠表示String的
        //  答案是調用上面的(int,String)(能夠理解爲上面的是專業的)。那麼有時候data做爲T類型傳入的就是String啊,豈不是就出問題了。這裏會在下方對應的public函數處理
        private ServerResponse(int status, T data) {
            this.status = status;
            this.data = data;
        }
    
        private ServerResponse(int status, String msg, T data) {
            this.status = status;
            this.msg = msg;
            this.data = data;
        }
    
        //    使之不在JSON序列化結果當中
        @JsonIgnore
        // 能夠快速進行成功與否的條件判斷
        public boolean isSuccess() {
            return this.status == ResponseCode.SUCCESS.getCode();
        }
    
        @JsonIgnore
        // 能夠快速進行成功與否的條件判斷,判斷false時,不用加!。囧
        public boolean isFail() {
            return this.status != ResponseCode.SUCCESS.getCode();
        }
    
        public int getStatus() {
            return status;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public T getData() {
            return data;
        }
    
        // 快速構建返回結果
        //    成功時的調用
        public static <T> ServerResponse<T> createBySuccess() {
            return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
        }
    
        public static <T> ServerResponse<T> createBySuccessMessage(String msg) {
            return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg);
        }
    
        public static <T> ServerResponse<T> createBySuccess(T data) {
            return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data);
        }
    
        public static <T> ServerResponse<T> createBySuccess(String msg, T data) {
            return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg, data);
        }
    
        //    失敗時的調用
        public static <T> ServerResponse<T> createByError() {
            return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc());
        }
    
        public static <T> ServerResponse<T> createByErrorMessage(String errorMessage) {
            return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage);
        }
    
        public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode, String errorMessage) {
            return new ServerResponse<T>(errorCode, errorMessage);
        }
    }

依賴:

lombok(絕對的效率工具,值得推薦)

應用:

package tech.jarry.learning.terminal.client;
    
    import com.renewable.terminal.terminal.common.ServerResponse;
    import com.renewable.terminal.terminal.entity.Terminal;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.List;
    
    /**
     * @Description:經過feign,對外提供termina服務的調用接口
     * @Author: jarry
     */
    @FeignClient(name = "terminal", fallback = TerminalClient.TerminalClientFallback.class)
    
    public interface TerminalClient {
    
        @PostMapping("/terminal/update_from_center.do")
        ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal);
    
        @PostMapping("/terminal/update.do")
        ServerResponse updateTerminal(@RequestBody Terminal terminal);
    
        @GetMapping("/terminal/refresh.do")
        ServerResponse refreshTerminal();
    
        @Component
        public static class TerminalClientFallback implements TerminalClient {
            @Override
            public ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal){
                return ServerResponse.createByErrorMessage("Busy service about Terminal/updateTerminalFromCenter().");
            }
    
            @Override
            public ServerResponse updateTerminal(Terminal terminal) {
                return ServerResponse.createByErrorMessage("Busy service about Terminal/updateTerminal().");
            }
    
            @Override
            public ServerResponse refreshTerminal(){
                return ServerResponse.createByErrorMessage("Busy service about Terminal/refreshTerminal().");
            }
        }
    }
package tech.jarry.learning.terminal.controller;
    
    
    import com.renewable.terminal.message.client.TerminalMessageClient;
    import com.renewable.terminal.terminal.common.ServerResponse;
    import com.renewable.terminal.terminal.entity.Terminal;
    import com.renewable.terminal.terminal.service.ITerminalService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    
    /**
     * <p>
     * 前端控制器
     * </p>
     *
     * @author jarry
     * @since 2019-07-22
     */
    @RestController
    @RequestMapping("/terminal/")
    public class TerminalController {
    
        @Autowired
        private ITerminalService iTerminalService;
    
        @GetMapping("get_terminal.do")
        @ResponseBody
        public ServerResponse getTerminal(){
            return iTerminalService.getTerminal();
        }
    
        @PostMapping("update.do")
        @ResponseBody
        public ServerResponse updateTerminal(@RequestBody Terminal terminal){
            boolean result = iTerminalService.updateById(terminal);
            iTerminalService.refresh();
            if (!result){
                return ServerResponse.createByErrorMessage("fail !");
            }
            return ServerResponse.createBySuccess(terminal);
        }
        @PostMapping("update_from_center.do")
        @ResponseBody
        public ServerResponse updateTerminalFromCenter(@RequestBody Terminal terminal){
            boolean result = iTerminalService.updateById(terminal);
            if (!result){
                return ServerResponse.createByErrorMessage("fail !");
            }
            return ServerResponse.createBySuccessMessage("success");
        }
    
        @GetMapping("refresh.do")
        @ResponseBody
        public ServerResponse refreshTerminal(){
            return iTerminalService.refresh();
        }
    
    }

問題:

在使用ServerResponse的過程當中,曾經遇到一個問題。
那就是ServerResponse在SpringCloud架構中的Feign中的RPC調用中,沒法進行反序列化。
找到的解釋是,缺少無參構造器(若是類中具備任意構造器,JVM就不會提供默認的無參構造器)。
因此在類的開頭增長了@NoArgsConstructor,使得類具有無參構造器,問題解決。

總結:

做爲服務器響應的統一數據格式,網上有不少的寫法。這個ServerResponse也不必定是最好的。即便是最好的,也不必定是最適合你的。

每每咱們在項目中須要一些工具實現一些特定功能,在實現功能以後,都或多或少會對現有的工具作一些調整,使得其更適合本身現有項目。

因此說,最好的不必定最適合。咱們須要根據現有的狀況,進行調整,重構,乃至自研。

題外話:

偷偷地推薦一下本身的我的博客。目前這個博客還處於測試階段。還有不少的調整,以後還會與我本身微信公衆號綁定,目測須要到今年下半年,才能所有完成。囧。有什麼意見也能夠提一提。

另外,因爲還在測試階段,因此若是哪天看不了,實屬正常。囧

相關文章
相關標籤/搜索