其實是我做了個開源工具(^__^),拿出來給大家鑑賞下,歡迎大家提意見
專案:github.com/xuyt11/x-ht… 歡迎關注和star 。
功能:這是一個http相關程式碼的建立工具。
現在我們每一次發版,基本上都會涉及到http相關的修改,以此來滿足發版的業務需求。
而在其中需要新增或修改的有http request、http request param、http response entity等其他相關的http程式碼。
而在多次的修改中,若前後端沒有協調好,就有可能會造成之後的返工、重複修改與線上bug量的增加等問題。
現在的痛點
如何解決每次發版時,都需要新增、修改http相關程式碼!
如何解決每次發版時,修改http相關程式碼造成的錯誤!
解決思路: 規範
其實很簡單,就是一個詞“規範”,任何事情,只要我們有了一定的規範,就會有一定的流程、可追蹤並且降低難度。
我相信99.99%的公司,都會有相關的http介面文件提供給前端同學,而且也會自己的一套規範(不論是我現在依賴的apidocjs,還是上家公司的doc檔案)。
當然,肯定也有口頭約定的情況,但這需要在之後,立即將約定轉化為文件,提供給前端的同學。git、http都可以作為提供的形式。我們依賴這個http的規範,就可以將http介面文件去解析轉義為x-http-wrapper內部的API資料。
- 再來就是依賴一定的規範(x-http-wrapper的模板檔案規範),將內部API資料轉化為http相關檔案。這樣,每次只要介面文件更新過後,我們就可以根據文件生成各個程式內部可以執行的程式碼。
這個功能與現在IDE中的getter、setter方法生成器功能其實是相同的原理!
x-http-wrapper介紹
- 這是一個http相關程式碼的建立工具。
現在能建立的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;} } }複製程式碼
- 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
快速使用入門
- 下載專案的Zip包,解壓縮,從xhwt資料夾下,選取其中的一個包裝器模板資料夾,作為目標包裝器的配置,該資料夾在下面都叫做target dir;
- 例如:xhwt/asynchttp/non_version(這是android-async-http庫的一個模板與配置);
- 獲取介面資料檔案(api_data.json:儲存apidocjs生成的API文件的資料)的路徑;
- 例如:guide資料夾中的api_data.json的絕對路徑
- 修改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" }複製程式碼
- 修改target dir中,API的模板檔案中\
\ 標籤內,生成檔案的目標路徑;- API的模板檔案是以.xhwt為字尾的檔案,是生成各個http相關檔案的模板;
- \
\ 標籤內,儲存的是模板檔案生成檔案的檔名稱與檔案地址;- 例如:
{ "file_name":"HttpApi.swift", "file_dirs":[ { "os_name":"Windows", "path":"生成檔案的目標路徑(絕對路徑)" }, { "os_name":"Mac OS X", "path":"生成檔案的目標路徑(絕對路徑)" } ] }複製程式碼
- 例如:
- 修改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" }, ... }複製程式碼
- 例如:若你想生成HttpApi型別的檔案,就需要將template_file_infos.HttpApi.need_generate設定為true,並要修改了xxx-httpapi.xhwt檔案中header標籤內的地址;
- 命令列生成相關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工具生成的資料檔案;
工作流程
- 解析x-http-wrapper.json這個配置檔案;
- 在配置檔案中,有API資料檔案(在api_data中),再根據配置資料,將API資料解析為x-http-wrapper中的model資料;
- 在配置檔案中,有所有的x-http-wrapper的template檔案(在template_file_infos中),根據template檔案中的內容與model datas和配置一起,生成目標檔案;
最新的jar
- 使用方式:
java -jar x-http-wrapper.jar xxx/x-http-wrapper.json複製程式碼
- x-http-wrapper.json檔案,必須是絕對路徑,該檔案是整個wrapper的配置檔案;
- 若有多個json檔案,也可以(如:有多個程式(ios,android)需要生成程式碼);
wrapper的配置檔案:
- x-http-wrapper.json (詳細的配置檔案介紹)
- 該檔案儲存有所有的配置資訊, 共有8個分類:
api_data, template_file_infos, base_config, filter, request, response, status_code, param_types
wrapper內部api資料模型
- BaseModel:
- 所有的model都需要繼承BaseModel
- BaseModel中有一個泛型用於儲存更高一級的BaseModel
- 在template engine中,反射只認BaseModel,不是BaseModel的model不能反射
- template engine在反射呼叫時,若沒有在反射的物件中找到方法,會從higherLevel中去找,直到沒有higherLevel為止;
- model的結構:
- VersionModel-->StatusCodeGroup, RequestGroup
- StatusCodeGroup-->StatusCode
- RequestGroup-->Request-->Url,Header,Input,Response
- Response-->Response File,Response Message
wrapper模板檔案的型別
- 所有的類別都在XHWTFileType列舉中,現階段共有6個類別;
- HttpApi, Request, RequestParam, Response, StatusCode, BaseResponse
- 且在該列舉中也有該模板類別所需資料的獲取過濾功能(getReflectiveDatas方法);
wrapper模板標籤
- 生成的檔案內容由該檔案型別獲取到的API資料與標籤兩者來驅動
- 頭部標籤\
\ : 用於標示該模板檔案,生成的目標檔案路徑和名稱;- file_dirs:目標檔案路徑
- file_name:目標檔名稱
- 現階段只有7個標籤型別:使用反射來進行資料的加工
- text, foreach, retain, list_single_line, if_else, list_replace, list_attach
- 標籤內部的匹配都為反射的方法名稱;
- 例如:在foreach標籤中
匹配的request_groups即為反射後去request_groups方法的資料,然後利用該資料去遍歷;<t:foreach each="request_groups"> </t:foreach>複製程式碼
- 例如:在foreach標籤中