首页 > 其他分享 >RPC请求&响应参数规范

RPC请求&响应参数规范

时间:2022-11-08 22:07:52浏览次数:33  
标签:code return 请求 响应 RPC static Result public String

§0. 有话先说

0.0 “开卷有益” -->如下是《码出高效:Java开发手册》书中5.3节的截图,文中提到“公司内部跨应用远程服务调用优先考虑使用Result对象来封装错误码、错误描述信息”

RPC请求&响应参数规范_数据

 

 

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

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(data transfer object)命名一样,有的人说用VO(value object)比较好,仁者见仁智者见智吧。


标签:code,return,请求,响应,RPC,static,Result,public,String
From: https://blog.51cto.com/u_15708799/5834948

相关文章