结构体作为接口的注意事项
Posted cherishui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构体作为接口的注意事项相关的知识,希望对你有一定的参考价值。
在后端向前端回复数据时,需要将结构化数据通过网络传输给前端,而网络传输是字节流传输,前端收到的是一段数据,那么,问题就落脚在如何解析这段数据。
很多请求的场景,返回的条数是动态变化的,比如订单数量。用户每下一个订单,那么请求返回的数量就会加1.这时候,如何较好的返回动态数据呢?这个看使用怎样的存储格式来承载可变长度的数据返回。就目前已知的处理方法:
- 使用 json 格式
- 使用 protobuf 格式
- 使用 struct 格式
由于工作需要,这里使用 struct 格式来进行数据的包裹。
固定长度的返回数据
比如,查询某个用户的账户余额,返回的结构体类似如下:
struct TAccountCapital : struct PacketHead
{
double m_dCapital;
}
struct PacketHead
为包头结构体,里面的内容为传输过程中的一些通用选项,在此不表。
当该结构体后续有扩展时,只要保证后续新增的字段始终放在后面,那就可以保持升级后新旧版本的兼容性。
可变长度的返回数据
比如,查询某个用户的当日订单信息,返回的结构体类似如下:
struct TAccountOrder :struct PacketHead
{
int m_nCount; // 订单个数
TOrderInfo m_OrderInfo[1]; // 订单数据
}
struct TOrderInfo
{
int m_nOrderNo; // 订单编号
string m_nOrderProductName; // 订单商品名称
}
这里用到的 linux下的 零长数组技巧,此处不再赘述。
客户端在接收到此类数据时,先通过 sizeof(TOrderInfo) * m_nCount
得到订单数据的真实长度,然后以sizeof(TOrderInfo)
为步长,将内存中数据整理输出,显示出来。
到这一步,看起来都很美好。
再往下看,随着业务的发展,订单信息需要新增一个字段,比如新增订单折扣信息。此时的返回结构体如下:
struct TOrderInfo
{
int m_nOrderNo; // 订单编号
string m_nOrderProductName; // 订单商品名称
double m_dOrderDiscount; // 订单折扣信息
}
一旦结构体有所改变,就存在前后端面对的结构体不一致,如果前端还按照原有的步长来截取内存输出,随后的订单信息就会全部错乱。
造成这个问题的原因是前端使用的结构体与后台的不一致,后台的修改带来了兼容性问题。既然问题根源知道了,那么解决的方法也有浮出水面了。那就是让前端知道真正的步长,这个步长不能依赖于前端能够看到的结构体长度,而要通过其他途径了解真实的长度,而这个真实的长度,只有后台了解,因此,需要后台增加一个字段,表示传递结构体真正的长度。
struct TAccountOrder :struct PacketHead
{
int m_nCount; // 订单个数
int m_nItemSize; // 订单数据结构体大小
TOrderInfo m_OrderInfo[1]; // 订单数据
}
结论
在使用结构体传递可变数据时,使用零长数组传输数据时,需要增加返回的结构体大小成员。
同时,要同前端开发人员做好约定,在解析数据时,要依赖接口中的结构体大小成员,而不要依赖自己算出来的结构体成员大小。
以上是关于结构体作为接口的注意事项的主要内容,如果未能解决你的问题,请参考以下文章