rpc請求&響應參數規范


§0. 有話先說

0.1 幾個概念

  • api-應用程序接口,最狹隘的定義,指的是我們程序里類或接口的方法。
  • rpc-局域網內部跨應用通信框架。常見的有dubbo、thrift、HSF、Feign。
  • rpcapi-基本上可以跟程序內部api一樣用法的api。

0.2 someword:

下面說的參數,包括請求參數和返回參數。

  1. 方法的參數通常不建議使用Object、Map,不易讀。
  2. 方法的參數如果都用String來傳遞JSON字符串或其他拼裝起來的字符串,不易讀是一方面,同時,潛在問題更多,你品,你細品。
  3. rpcapi,建議自行處理異常,別拋給調用方。
  4. 方法的參數,使用枚舉會提高可讀性 @see
  5. 。。。

0.3 為什么有本文?

書接前文,rpcapi雖然可以像程序內部api一樣,支持java各種數據類型,可以像調用程序內部api一樣調用rpcapi。不過,所不同的是,rpcapi應該處理各種可能的異常情況,而不是拋出異常。這使得rpcapi與restapi一樣,不能只是返回數據(資源),而應該對可能出現的異常情況進行判斷,比如參數合法性,數據是否存在,數據狀態,程序異常,等等,當沒有異常情況時,才返回所需數據(資源)。因此,就有了本文下文說的Result<T>。

§1. 請求參數

1.1 如果參數比較少,比如少於3個,可以顯式定義出來。比如

getEnterpriseById(String enterpriseId)

getEnterpriseById(String enterpriseId,ProductEnum product)

 

1.2 如果參數超過3個,建議定義一個DTO。rpc傳輸對象暫定統一命名為DTO。請求dto命名建議以ReqDTO結尾。當然,如果請求響應都使用相同的dto的話,就直接以DTO結尾也未嘗不可。比如

addEnterprise(EnterpriseDTO enterprise);

selectEnterprise(EnterpriseDTO enterprise);

§2. 響應參數

2.1 首先,響應參數統一使用Result<T>。即,返回值統一使用泛型。Result<T>主要成員有3個:

  • int code - 響應碼,成功統一是200. 對應的code枚舉定義在ResultCodeEnum.java里
  • String message - 響應描述,尤其是當非200的情況下,需要指出錯誤信息。
  • T result - 響應數據。一般在code=200的情況下會設定result。

Result<T>相關操作方法后文贅述。

2.2 其次,對於返回數據來說,同1.1、1.2所述。

2.2.1 如果比較單一,比如就返回一個交易量,可以是Result<Integer> selectTransCount(...)。

2.2.2 如果返回數據比較復雜,可以定義一個DTO,響應dto命名建議以RespDTO結尾。當然,如果請求響應都使用相同的dto的話,就直接以DTO結尾也未嘗不可。

2.2.2.1 如果你不願意定義一個dto對象,也行。

可以考慮返回JSON對象(JSON對象哦,不是JSON字符串),即Result<JSONObject>。

也可以考慮返回Map對象,比如Result<Map<String,Object>>。瞧瞧,使用map,有些情況下,就不可避免的涉及到Object。使用Object來傳參或作為方法返回值是大忌,與CV大法一樣多少都會令人詬病。

2.2.3 對於返回集合的情況,當然也很簡單,無非就是Result<List<EnterpriseDTO>>了唄。

 

§3. 強大的Result<T>

Result<T>是一個泛型類,ResultCodeEnum定義了code的枚舉項,它們定義在com.emax.zhenghe:zhenghe-rpcapistyle包里。

maven dependency依賴:

< dependency >
     < groupId >com.emax.zhenghe</ groupId >
     < artifactId >zhenghe-rpcapistyle</ artifactId >
     < version >1.0.1-SNAPSHOT</ version >
</ dependency >

 

Result<T>重要成員方法:

Result<T>主要操作方法是設置返回結果的(這不是廢話嘛~),返回分兩種,成功的返回,錯誤的返回。因此,Result<T>定義了兩類方法,public static Result<T> success(...)和public static Result<T> err(...)。 當然,為了方便大家使用,方法重載是免不了的。詳細見下面列表,總有一款適合你!

▄︻┻┳═一 public static <T> Result<T> success()
▄︻┻┳═一 public static <T> Result<T> success(T data)
▄︻┻┳═一 public static <T> Result<T> success(T data, String msg)
▄︻┻┳═一 public Result<T> successWithMsg(String message)
▄︻┻┳═一 public static <T> Result<T> err(String msg)
▄︻┻┳═一 public static <T> Result<T> err(ResultCodeEnum code, String msg)
▄︻┻┳═一 public static <T> Result<T> err(int code, String msg)

 

這個class的實現代碼呢, 一睹芳容吧!

public class Result<T> implements Serializable {
 
    /**
     * 返回代碼
     */
    private int code = 0;
 
    /**
     * 返回處理消息
     */
    private String message = "操作成功!";
 
    /**
     * 返回數據對象 data
     */
    private T result;
     
     
    // 服務提供方的操作方法-----開始
    public static <T> Result<T> success(T data){
        return success(data, "成功");
    }
    public static <T> Result<T> success(T data, String msg) {
        Result<T> r = new Result<>();
        r.setSuccess(true);
        r.setCode(CommonConstant.SC_OK_200);
        r.setResult(data);
        return r;
    }
    public static <T> Result<T> success() {
        Result<T> r = new Result<>();
        r.setSuccess(true);
        r.setCode(CommonConstant.SC_OK_200);
        r.setMessage("成功");
        return r;
    }
    public Result<T> successWithMsg(String message){
        this.message = message;
        this.code = CommonConstant.SC_OK_200;
        this.success = true;
        return this;
    }  
 
    public static <T> Result<T> err(String msg) {
        return err(ResultCodeEnum.INTERNAL_SERVER_ERROR, msg);
    }
 
    public static <T> Result<T> err(ResultCodeEnum code, String msg) {
        return err(code.getCode(), StringUtils.isBlank(msg) ? code.getMsg() : msg);
    }
 
    public static <T> Result<T> err(int code, String msg) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setMessage(msg);
        r.setSuccess(false);
        return r;
    }
    // 服務提供方的操作方法-----結束
     
     
    // 服務提供方的操作方法-----開始
    // 客戶端接收到響應后,可以使用isSuccess來判斷是否成功,成功后,可以獲取返回數據進行后續處理;不成功,則可以 getCode()和 getMessage()來記錄code和msg。
    public boolean isSuccess() {
        return code == ResultCodeEnum.SUCCESS.getCode();
    }
    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
    public T getResult() {
        return this.result;
    }
    // 服務提供方的操作方法-----結束
}

 

對擴展開放

注意到了嗎?Result的code是int類型,不是上文提到的ResultCodeEnum枚舉。

why?code定義成ResultCodeEnum當然是再好不過了,程序使用枚舉要比數字易讀多了。為什么不這么做呢?——為了考慮擴展。試想,如果現有ResultCodeEnum的枚舉項不滿足你的項目需要,你是不是要增加ResultCodeEnum枚舉項?是的,那么,這時,就要去修改zhenghe-rpcapistyle的源碼,而zhenghe-rpcapistyle在另一個項目里。隨着依賴zhenghe-rpcapistyle的項目的逐漸增多,ResultCodeEnum也許將變得尤其難於使用。

因此,如果zhenghe-rpcapistyle里ResultCodeEnum不滿足項目需要,大家可以在你的項目里自行定義一個ResultCodeEnum.java,或者可以在系統的常量類里定義code常量(推薦前者)。

§4. How to use?

如下testcase在zhenghe-rpcapistyle包里,可以幫助你快速了解並掌控Result<T>。

package  com.emax.zhenghe.common.api.vo;
 
import  com.alibaba.fastjson.JSON;
import  lombok.extern.slf4j.Slf4j;
 
import  java.util.Arrays;
import  java.util.List;
 
@Slf4j
public  class  ResultTest {
     public  static  void  main(String[] args) {
         //Result里保存Long型數值
         Result<Long> longResult = Result.success(5L,  "" );
         System.out.println( "longResult="  + longResult);
         System.out.println(longResult.getCode() +  "----" );
         longResult.setResult(1L);
         System.out.println(longResult.getCode());
 
         //Result里存儲數據集合
         Result<List<Integer>> listResult = Result.success(Arrays.asList( 1 2 4 ));
         System.out.println(JSON.toJSONString(listResult.getResult()));
 
         //如下Result相當於Result<Object>或Result<?>
         Result objectResult = Result.success();
         System.out.println( "objectResult.getCode()="  + objectResult.getCode());
 
         //在某些error情況下,Result要設置響應數據。
         Result<Integer> integerResult = Result.err( "sdfsda" );
         integerResult.setResult( 1 );
         System.out.println( "integerResult="  + integerResult);
 
         //構造器初始化的Result對象,code的默認值是200(成功)
         Result<Integer> newResult =  new  Result<>();
         newResult.setCode( 404 );
         newResult.setResult(Integer.MIN_VALUE);
         System.out.println(newResult.getResult() +  "---"  + newResult.getCode());
     }
 
}

 

 

下面,以一個簡單的示例來介紹rpc服務的實現類里如何使用Result<T>

@Override
public  Result<Long> separateFeeQuery() {
     Long count=0L; //todo:調用service讀庫       
     return  Result.success(count);
}

又例如:

@Override
public  Result addEnterprise(EnterpriseDTO enterprise) {
     ....
     boolean  ok = enterpriseService.save(po);
     if  (ok){
         return  Result.success();
     else  {
         return  Result.err( "數據保存失敗" );
     }
}

 

§5. 命名規范

方法命名可能還真不好統一,也不好立規范。

同樣rpcapi接口類名也是如此,比如可以統一以-Api結尾,也可以統一以-Service結尾,不過最好與工程里的Service區分開來,這樣便於程序理解。

就像上面§1提到的數據傳輸對象用DTO命名一樣,有的人說用VO比較好,仁者見仁智者見智吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM