x-http-wrapper: 如何解决每次发版时,修改 http 相关代码造成的错误!(Android、iOS、h5) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
请不要在回答技术问题时复制粘贴 AI 生成的内容
xuyt

x-http-wrapper: 如何解决每次发版时,修改 http 相关代码造成的错误!(Android、iOS、h5)

  •  
  •   xuyt
    xuyt11 Jan 16, 2017 2527 views
    This topic created in 3390 days ago, the information mentioned may be changed or developed.

    其实是我做了个开源工具(^__^),拿出来给大家鉴赏下,欢迎大家提意见
    项目: https://github.com/xuyt11/x-http-wrapper 欢迎关注和 star 。
    功能:这是一个 http 相关代码的创建工具。

    现在我们每一次发版,基本上都会涉及到 http 相关的修改,以此来满足发版的业务需求。
    而在其中需要添加或修改的有 http request 、 http request param 、 http response entity 等其他相关的 http 代码。
    而在多次的修改中,若前后端没有协调好,就有可能会造成之后的返工、重复修改与线上 bug 量的增加等问题。

    现在的痛点

    如何解决每次发版时,都需要新增、修改 http 相关代码!
    如何解决每次发版时,修改 http 相关代码造成的错误!

    解决思路: 规范

    1. 其实很简单,就是一个词“规范”,任何事情,只要我们有了一定的规范,就会有一定的流程、可追踪并且降低难度。
      我相信 99.99%的公司,都会有相关的 http 接口文档提供给前端同学,而且也会自己的一套规范(不论是我现在依赖的 apidocjs ,还是上家公司的 doc 文件)。
      当然,肯定也有口头约定的情况,但这需要在之后,立即将约定转化为文档,提供给前端的同学。 git 、 http 都可以作为提供的形式。

    2. 我们依赖这个 http 的规范,就可以将 http 接口文档去解析转义为 x-http-wrapper 内部的 API 数据。

    3. 再来就是依赖一定的规范(x-http-wrapper 的模板文件规范),将内部 API 数据转化为 http 相关文件。这样,每次只要接口文档更新过后,我们就可以根据文档生成各个程序内部可以运行的代码。
      这个功能与现在 IDE 中的 getter 、 setter 方法生成器功能其实是相同的原理!

    x-http-wrapper 介绍

    1. 这是一个 http 相关代码的创建工具。
    2. 现在能创建的 http 相关的文件类型有: http 请求分类, http 请求,请求方法参数,响应实体,响应实体中状态码列表和基础响应实体类。
      • HttpApi( http 请求分类): 所有 API 请求的统一调用入口,统合所有的请求类别的接口,防止 API 接口分散。
      public class HttpApi { private static Account account; private static Data data; private static Message message; public static Account account() { if (null == account) { account = Account.getInstance(); } return account; } public static Data data() { if (null == data) { data = Data.getInstance(); } return data; } public static Message message() { if (null == message) { message = Message.getInstance(); } return message; } } 
      • Request( http 请求): 单个请求分组中,所有的请求方法。
      public class Account extends BaseApi { public static Account getInstance() { return Helper.instance; } private static class Helper { public static final Account instance = new Account(); } private Account() { super(); } /** * @version 2.0.0 * @requestUrl * @title 初始化账号信息 * */ public RequestHandle init(Context cxt001, ResponseHandlerInterface response) { // hide implementation } /** * @version 2.0.0 * @title 扫二维码到 web 端进行操作 * * @param context String desc * @param project_id isOptional Integer desc * @param scene isOptional String desc * @param uuid_rand String desc */ public RequestHandle qrcodeConfirm(Context cxt001, String context, Integer project_id, String scene, String uuid_rand, ResponseHandlerInterface response) { // hide implementation } /** * 缩略请求方法 */ public RequestHandle qrcodeConfirm(Context cxt001, QrcodeConfirmRP.Parameter parameter, ResponseHandlerInterface response) { return qrcodeConfirm(cxt001, parameter.context, parameter.project_id, parameter.scene, parameter.uuid_rand, response); } } 
      • RequestParam(请求方法参数): 请求参数分组归类,对应单个请求,用于请求参数较多的情况,生成请求参数的分类实体类(请求参数也肯能有多个分类),减少请求方法的输入参数。
      /** * 请求方法参数 */ public class QrcodeConfirmRP implements Serializable { public static final class Parameter implements Serializable { /** * type: String<br> * isOptional : false<br> * desc: <p>扫码场景,枚举值</p> */ public String context; /** * type: Integer<br> * isOptional : true<br> * desc: <p>业务参数: 根据 context 的不同而不同</p> */ public Integer project_id; /** * type: String<br> * isOptional : true<br> * desc: <p>身份信息: 服务端会优先使用客户端传入的身份信息,当为”投资人“的时候必传</p> */ public String scene; /** * type: String<br> * isOptional : false<br> * desc: <p>从二维码扫描得到的唯一码</p> */ public String uuid_rand; } } 
      • Response(响应实体): 请求的相应数据 model
      public class Init { private long member_id; private long member_role; private long member_status; private String ry_token; private long step; public long getMemberId() {return member_id;} public long getMemberRole() {return member_role;} public long getMemberStatus() {return member_status;} public String getRyToken() {return ry_token;} public long getStep() {return step;} public void setMemberId(long member_id) {this.member_id = member_id;} public void setMemberRole(long member_role) {this.member_role = member_role;} public void setMemberStatus(long member_status) {this.member_status = member_status;} public void setRyToken(String ry_token) {this.ry_token = ry_token;} public void setStep(long step) {this.step = step;} } 
      • StatusCode(响应实体中状态码列表): 响应中所有状态码的枚举类
      public class StatusCode { /** '') */ public static final int OK = 0; /** '登录状态已过期,请重新登入') */ public static final int UNAUTHORIZED = 101; /** '您没有权限查看') */ public static final int FORBIDDEN = 102; /** '资源未找到') */ public static final int NOT_FOUND = 103; /** '客户端请求错误') # 4XX 客户端错误 */ public static final int CLIENT_ERROR = 228; /** '服务器错误') # 5XX 服务器错误 */ public static final int SERVER_ERROR = 229; /** '参数错误') */ public static final int PARAM_ERROR = 230; /** '登录失败,请检查您的邮箱地址是否正确') */ public static final int LOGIN_FAIL_EMAIL_NOT_EXIST = 332; /** '登录失败,请确认您的手机号是否正确') */ public static final int LOGIN_FAIL_MOBILE_NOT_EXIST = 333; /** '登录失败,请检查密码是否正确') */ public static final int LOGIN_FAIL_PASSWORD_ERROR = 334; } 
      • BaseResponse(基础响应实体类): 基础的响应实体类
      public class ResponseEntity<T> { private int status_code; private String message; private Error error; private T data; public int getStatusCode() {return status_code;} public void setStatusCode(int status_code) {this.status_code = status_code;} public String getMessage() {return message;} public void setMessage(String message) {this.message = message;} public Error getError() {return error;} public void setError(Error error) {this.error = error;} public T getData() {return data;} public void setData(T data) {this.data = data;} public static class Error { private String detail; private List<String> device_token; private List<String> content; private List<String> followed_id; public String getDetail() {return detail;} public void setDetail(String detail) {this.detail = detail;} public List<String> getDeviceToken() {return device_token;} public void setDeviceToken(List<String> device_token) {this.device_token = device_token;} public List<String> getContent() {return content;} public void setContent(List<String> content) {this.cOntent= content;} public List<String> getFollowedId() {return followed_id;} public void setFollowedId(List<String> followed_id) {this.followed_id = followed_id;} } } 
    3. http 的数据来源,现阶段只有 apidocjs 这一个
      • 若有其他数据来源,可以配置 api_data.source 属性,然后添加对应的解析器,解析为 xhw 的 model 。

    工具环境与依赖

    • 命令行运行 jar 文件: 需要 java8 及以上的版本
    • 开发环境:
      • Java 的版本: java8 及以上的版本
      • 开发平台: intellij idea
      • 依赖的 jar: gson:2.8.0, rxjava:1.2.2, junit:4.12

    快速使用入门

    1. 下载项目的 Zip 包,解压缩,从 xhwt 文件夹下,选取其中的一个包装器模板文件夹,作为目标包装器的配置,该文件夹在下面都叫做target dir
      • 例如: xhwt/asynchttp/non_version(这是 android-async-http 库的一个模板与配置);
    2. 获取接口数据文件(api_data.json:存储 apidocjs 生成的 API 文档的数据)的路径;
      • 例如: guide 文件夹中的 api_data.json 的绝对路径
    3. 修改 target dir 下配置文件(x-http-wrapper.json)中 api_data.file_path_infos 的配置信息,将 api_data.json 的绝对路径添加上去;
       "api_data": { "source": "apidocjs", "file_path_type": "file", "file_path_infos": [ { "os_name": "Mac OS X", "path": "api_data.json 的绝对路径" }, { "os_name": "Windows", "path": "api_data.json 的绝对路径" } ], "file_charset": "UTF-8" } 
    4. 修改 target dir 中, API 的模板文件中<t:header></t:header>标签内,生成文件的目标路径;
      • API 的模板文件是以.xhwt 为后缀的文件,是生成各个 http 相关文件的模板;
      • <t:header></t:header>标签内,保存的是模板文件生成文件的文件名称与文件地址;
        • 例如:
        { "file_name":"HttpApi.swift", "file_dirs":[ { "os_name":"Windows", "path":"生成文件的目标路径(绝对路径)" }, { "os_name":"Mac OS X", "path":"生成文件的目标路径(绝对路径)" } ] } 
    5. 修改 target dir 下配置文件(x-http-wrapper.json)中 template_file_infos 中的 need_generate 属性,用于开启、关闭生成文件的功能;
      • 例如:若你想生成 HttpApi 类型的文件,就需要将 template_file_infos.HttpApi.need_generate 设置为 true ,并要修改了 xxx-httpapi.xhwt 文件中 header 标签内的地址;
       "template_file_infos": { "HttpApi": { "need_generate": true, "path": "ncm_ios_n-httpapi.xhwt" }, ... } 
    6. 命令行生成相关 http 文件
      • 命令行运行: java -jar (jar 文件的路径) (配置文件的绝对路径)
      • jar 文件的路径:在 guide 文件夹下有最新的 jar(x-http-wrapper.jar)
      • 配置文件的绝对路径:配置文件(x-http-wrapper.json)的绝对路径
      java -jar x-http-wrapper.jar xxxx/x-http-wrapper.json 

    api 的数据源:apidocjs

    • api_data.json 就是使用 apidocjs 工具生成的数据文件;

    工作流程

    1. 解析 x-http-wrapper.json 这个配置文件;
    2. 在配置文件中,有 API 数据文件(在 api_data 中),再根据配置数据,将 API 数据解析为 x-http-wrapper 中的 model 数据;
    3. 在配置文件中,有所有的 x-http-wrapper 的 template 文件(在 template_file_infos 中),根据 template 文件中的内容与 model datas 和配置一起,生成目标文件;

    最新的 jar

    1. 使用方式:
    java -jar x-http-wrapper.jar xxx/x-http-wrapper.json 
    1. x-http-wrapper.json 文件,必须是绝对路径,该文件是整个 wrapper 的配置文件;
    2. 若有多个 json 文件,也可以(如:有多个程序(ios,android)需要生成代码);

    wrapper 的配置文件:

    wrapper 内部 api 数据模型

    1. BaseModel:
      • 所有的 model 都需要继承 BaseModel
      • BaseModel 中有一个泛型用于存储更高一级的 BaseModel
      • 在 template engine 中,反射只认 BaseModel ,不是 BaseModel 的 model 不能反射
      • template engine 在反射调用时,若没有在反射的对象中找到方法,会从 higherLevel 中去找,直到没有 higherLevel 为止;
    2. model 的结构:
      • VersionModel-->StatusCodeGroup, RequestGroup
      • StatusCodeGroup-->StatusCode
      • RequestGroup-->Request-->Url,Header,Input,Response
      • Response-->Response File,Response Message

    wrapper 模板文件的类型

    1. 所有的类别都在 XHWTFileType 枚举中,现阶段共有 6 个类别;
      • HttpApi, Request, RequestParam, Response, StatusCode, BaseResponse
    2. 且在该枚举中也有该模板类别所需数据的获取过滤功能(getReflectiveDatas 方法);

    wrapper 模板标签

    1. 生成的文件内容由该文件类型获取到的 API 数据与标签两者来驱动
    2. 头部标签<t:header></t:header>: 用于标示该模板文件,生成的目标文件路径和名称;
      • file_dirs:目标文件路径
      • file_name:目标文件名称
    3. 现阶段只有 7 个标签类型:使用反射来进行数据的加工
      • text, foreach, retain, list_single_line, if_else, list_replace, list_attach
      • 标签内部的匹配都为反射的方法名称;
        • 例如:在 foreach 标签中
        <t:foreach each="request_groups"> </t:foreach> 
        匹配的 request_groups 即为反射后去 request_groups 方法的数据,然后利用该数据去遍历;
    No Comments Yet
    About     Help     Advertise     Blog     API     FAQ     Solana     4360 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 50ms UTC 00:10 PVG 08:10 LAX 17:10 JFK 20:10
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86