APIJSON, let interfaces and documents go to hell !, Apijson
Me:
APIJSON, let interfaces and documents go to hell!
Https://github.com/TommyLemon/APIJSON
Server:
What?
Client:
What is APIJSON?
Me:
APIJSON is a JSON Transmission Structure protocol.
The client can define any JSON structure to initiate a request to the server, and the server returns the JSON string of the corresponding structure.
Any data structure in one request is convenient and flexible, and no special interface or multiple requests are required.
Supports addition, deletion, modification, and query, fuzzy search, and remote function calls. It can also remove duplicate data, saving traffic and improving the speed!
From now on, there is no interface for transmitting JSON data through HTTP, and no documentation is required!
The client no longer needs to communicate with the server about interfaces or documentation issues! No more errors in the document!
The server no longer needs to write new interfaces and documents to be compatible with old clients! The client will never get bored anytime, anywhere!
Client:
Is this APIJSON so good? How can this problem be achieved?
Me:
For example ):
Request:
{"[]": {// Request an array "page": 0, // array condition "count": 2, "User ": {// query the table named User in the request, and return the JSONObject "sex": 0 // object condition}, "Moment": {"userId @": "/User/id" // default dependency path, starting from the path of the same level object}, "Comment []": {// request an array named Comment "page ": 0, "count": 2, "Comment": {"momentId @": "[]/Moment/id" // complete dependency path }}}}
Click here to test
Return Value:
{ "[]":{ "0":{ "User":{ "id":38710, "sex":0, "phone":"1300038710", "name":"Name-38710", "head":"http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000" }, "Moment":{ "id":470, "title":"Title-470", "content":"This is a Content...-470", "userId":38710, "pictureList":["http://static.oschina.net/uploads/user/585/1170143_50.jpg?t=1390226446000"] }, "Comment[]":{ "0":{ "Comment":{ "id":4, "parentId":0, "momentId":470, "userId":310, "targetUserId":14604, "content":"This is a Content...-4", "targetUserName":"targetUserName-14604", "userName":"userName-93781" } }, "1":{ "Comment":{ "id":22, "parentId":221, "momentId":470, "userId":332, "targetUserId":5904, "content":"This is a Content...-22", "targetUserName":"targetUserName-5904", "userName":"userName-11679" } } } }, "1":{ "User":{ "id":70793, "sex":0, "phone":"1300070793", "name":"Name-70793", "head":"http://static.oschina.net/uploads/user/1174/2348263_50.png?t=1439773471000" }, "Moment":{ "id":170, "title":"Title-73", "content":"This is a Content...-73", "userId":70793, "pictureList":["http://my.oschina.net/img/portrait.gif?t=1451961935000"] }, "Comment[]":{ "0":{ "Comment":{ "id":44, "parentId":0, "momentId":170, "userId":7073, "targetUserId":6378, "content":"This is a Content...-44", "targetUserName":"targetUserName-6378", "userName":"userName-88645" } }, "1":{ "Comment":{ "id":54, "parentId":0, "momentId":170, "userId":3, "targetUserId":62122, "content":"This is a Content...-54", "targetUserName":"targetUserName-62122", "userName":"userName-82381" } } } } }}
Client:
It is clear that you don't need to read the document!
I have been put into this document for many times. I have written or written less one field, wrong field or multiple spaces, or wrong field type in this document, I don't know how much debugging and communication time is wasted!
Sometimes an error occurs when I use the app on the top. After debugging, I found that the interface was changed on the server! We were not notified in time!
Once I had a hard time deciding whether to change a single-layer comment to QQ-type multi-level comment. I wrote a demo demonstration based on the previous interface and showed it to the above-mentioned comment. I was satisfied with the above decision to achieve the requirement, as a result, the backend did not discuss with me and changed the json structure returned by the interface. As a result, I had to refactor the parsing code on this side. It was really drunk!
Me:
With APIJSON, you can customize the returned JSON structure as needed. Without interfaces and documents, the returned JSON structure will not be compromised, nor will there be any problem with the reconstruction of the client caused by setting the JSON structure on the backend. Haha!
Client:
Nice!
Server:
Some interfaces require currentUserId and loginPassword. What do you do?
Me:
Directly transfer data to the outermost layer. For example:
{ "currentUserId":100, "loginPassword":1234, "User":{ "id":1 }}
Server:
Where are the returned status codes and prompts?
Me:
It is also in the outermost layer, for example, the returned results of the above request:
{ "status":200, "message":"success", "User":{ "id":"1", "sex":"0", "phone":"1234567890", "name":"Tommy", "head":"http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000" }}
Client:
Any data structure in one request is convenient and flexible, and no special interface or multiple requests are required?
In the past, I made an interface. The upper part is the user information, and the lower part is his recent dynamics. A maximum of three displays, similar to the details.
I need to request two times:
User:
http://www.xxx.com/get/user?id=100
Moment list:
http://www.xxx.com/get/moment/list?page=0&count=3&userId=100
Can this be done now:
User and Moment list:
http://www.xxx.com/get/{"User":{"id":100}, "[]":{"page":0, "count":3, "Moment":{"userId":100}}}
Me:
That's right.
Client:
Okay. How can we remove duplicate data?
Me:
For example, there is a dynamic list in the QQ space. Each dynamic page contains the User and the Corresponding Dynamic Content Moment.
If you enter a person's space, it will be his dynamic.
In the list data returned in the traditional way, each dynamic record contains the same User, resulting in data duplication:
Request:
http://www.xxx.com/get/moment/list?page=0&count=5&userId=100
Return Value:
{ "status":200, "message":"success", "data":[ { "id":1, "content":"xxx1", ..., "User":{ "id":100, "name":"Tommy", ... } }, { "id":2, "content":"xxx2", ..., "User":{ "id":100, "name":"Tommy", ... } }, ... ]}
There are 5 duplicate users.
APIJSON saves four duplicate users:
Request:
http://www.xxx.com/get/{"User":{"id":100}, "[]":{"page":0, "count":5, "Moment":{"userId":100}}}
Return Value:
{ "status": 200, "message": "success", "User": { "id": 100, "name": "Tommy", ... }, "[]": { "0": { "Moment": { "id": 1, "content": "xxx1", ... } }, "1": { "Moment": { "id": 2, "content": "xxx2", ... } }, ... }}
If you have already obtained the User, you can also save all duplicate users as follows:
Request:
http://www.xxx.com/get/{"[]":{"page":0, "count":5, "Moment":{"userId":100}}}
Return Value:
{ "status": 200, "message": "success", "[]": { "0": { "Moment": { "id": 1, "content": "xxx1", ... } }, "1": { "Moment": { "id": 2, "content": "xxx2", ... } }, ... }}
Client:
Traditionally, the server can also add a returned format field to the interface. Based on this field, determine whether to remove duplicate users.
Me:
Yes, but this will cause the following problems:
1. The server should add fields and judgment fields to return code of different data.
2. The server should add relevant instructions in the document.
3. This function is not necessarily useful, because the UI requirements of the client are often very easy to change, leading to the lack of conditions for using this function. For one or two versions, it is not worthwhile for the server and the client.
The use of APIJSON does not solve these problems, because interfaces or documents are not required at all! Whether or not to repeat the client depends on the client's wishes. The server does not need to do anything.
Client:
This way, like!
By the way, does APIJSON provide a missing function compared to the traditional one?
Me:
APIJSON can be used in traditional methods.
Http json interaction between the client and the server:
Client-encapsulate request-> server-Parse request-generate response-> client-Parse response
Traditional request:
base_url/lowercase_table_name?key0=value0&key1=value1...
APIJSON request:
base_url/{TableName:{key0:value0, key1:value1...}}
TableName corresponds to lowercase_table_name, and key: value corresponds to key = value, which all correspond strictly. Therefore, the information contained in a traditional request can be included in the same APIJSON request, APIJSON can also be implemented in the traditional way.
Client:
Okay.
Server:
How does APIJSON ensure the data consistency that the server returns to clients of different versions?
For example, if the value returned by an interface in my previous version is a, the current version must return a + B to all versions of the client. In the traditional method, the server only needs to change the return value of this interface, you do not need to change both interfaces and clients.
If APIJSON is used, a and B are returned for some versions, so they cannot be unified?
Me:
The Request Parsing and response operations of APIJSON are completed on the Server, corresponding to the project in APIJSON (Server.
The server can intercept related requests. For example, if the value of request a is changed to a + B, a + B is returned for All Client versions. There is no need to change the code on the client. As for interfaces, there is no interface at all.
Server:
What about my inconsistency? Return different values for clients of different versions.
Me:
First of all, there are very few such requirements. For example, to reduce the price of movie tickets, you cannot lower the price in the new client version. Is the previous version still the original price?
If this is the case, the client can send the version number in the request. The server returns different values based on the version number.
Server:
Yes. So how do I use APIJSON for permission processing? Some data requires relevant permissions for operation. For example, You need to log on to the account and pay for the account.
Me:
After the server obtains the request from the client, it uses a permission verification class to verify whether there is Operation permission before the corresponding table is operated. Otherwise, an error message is returned.
The permission verification class can be like this:
Package zuo. biao. apijson. server. SQL; import java. rmi. accessException; import com. alibaba. fastjson. JSONObject; import zuo. biao. apijson. stringUtil;/** permission verification class * @ author Lemon */public class AccessVerifyer {private static final String TAG = "AccessVerifyer:"; private static final int ACCESS_LOGIN = 1; private static final int ACCESS_PAY = 2; public static final String KEY_CURRENT_USER_ID = "currentUserId"; public static final String KEY_LOGIN_PASSWORD = "loginPassword"; public static final String KEY_PAY_PASSWORD = "payPassword "; public static final String [] LOGIN_ACCESS_TABLE_NAMES = {"Work", "Comment"}; public static final String [] PAY_ACCESS_TABLE_NAMES = {"Wallet "}; /** verify whether the permission passes * @ param request * @ param tableName * @ return */public static boolean verify (JSONObject request, String tableName) throws AccessException {try {verify (request, getAccessId (tableName);} catch (AccessException e) {throw new AccessException (TAG + "verify tableName =" + tableName + ", error =" + e. getMessage ();} return true;}/** verify whether the permission passes * @ param request * @ param accessId. You can directly write ACCESS_LOGIN in the code or create an Access table, tableName list of the table with id and permission change * @ return * @ throws AccessException */public static boolean verify (JSONObject request, int accessId) throws AccessException {if (accessId <0 | request = null) {return true;} long currentUserId = request. getLongValue (KEY_CURRENT_USER_ID); if (currentUserId <= 0) {throw new AccessException (TAG + "verify accessId =" + accessId + "> currentUserId <= 0, currentUserId = "+ currentUserId);} String password; switch (accessId) {case ACCESS_LOGIN: password = StringUtil. getString (request. getString (KEY_LOGIN_PASSWORD); if (password. equals (StringUtil. getString (getLoginPassword (currentUserId) = false) {throw new AccessException (TAG + "verify accessId =" + accessId + "> currentUserId or loginPassword error" + "currentUserId =" + currentUserId + ", loginPassword = "+ password);} case ACCESS_PAY: password = StringUtil. getString (request. getString (KEY_PAY_PASSWORD); if (password. equals (StringUtil. getString (getPayPassword (currentUserId) = false) {throw new AccessException (TAG + "verify accessId =" + accessId + "> currentUserId or payPassword error" + "currentUserId =" + currentUserId + ", payPassword = "+ password);} default: return true;}/** get permission id * @ param tableName * @ return */public static int getAccessId (String tableName) {if (StringUtil. isNotEmpty (tableName, true) = false) {return-1 ;}for (int I = 0; I <LOGIN_ACCESS_TABLE_NAMES.length; I ++) {if (tableName. equals (LOGIN_ACCESS_TABLE_NAMES [I]) {return ACCESS_LOGIN ;}}for (int I = 0; I <PAY_ACCESS_TABLE_NAMES.length; I ++) {if (tableName. equals (PAY_ACCESS_TABLE_NAMES [I]) {return ACCESS_PAY;} return-1 ;} /** get logon password * @ param userId * @ return */public static String getLoginPassword (long userId) {// TODO query and return the login password return "123456" for the corresponding userId "; // test only}/** get the payment password * @ param userId * @ return */public static String getPayPassword (long currentUserId) {// TODO query and return the payment password return "123456" for the corresponding userId; // test only }}
Server:
Well, it does. After reading the introduction on the project homepage, I feel that APIJSON is indeed very powerful and convenient. I don't even need to write any interfaces or documents, nor will I suddenly receive a call from the client when I am in fitness or watching movies with my girlfriend.
However, I still have a problem. APIJSON dynamically concatenates SQL statements and is indeed flexible, but will it cause SQL Injection problems?
Me:
APIJSON concatenates SQL statements on the server, and the client cannot directly send the SQL statements to the server. The entire database operation is completely controllable on the server side. The server side can intercept dangerous injection, and the risk is no higher than the traditional method.
Server:
My brother! I want to download it. Haha!
Client:
Haha, I also want to try it. How can I get the source code? Is it free?
Me:
It is already open-source in Github and open-source China and is completely free of charge.
Github: https://github.com/TommyLemon/APIJSON
Open source China: http://git.oschina.net/Lemon19950301/APIJSON
Server:
Great! Star already exists!
Client:
Star + 1. By the way, Fork also made a research! In addition, the document is very detailed!
Me:
If you have any questions or suggestions, please ask issue or send me an email tommylemon@qq.com. Let's discuss it together!
Server:
I don't need to write a lot of interfaces in the future. I don't need to write compatible code or document. You can focus on data processing, monitoring, statistics, and analysis. Haha!
Client:
I don't have to wait for the server to write the interface to make the request. Now I have customized the returned JSON and read the returned JSON structure. I can directly write the parsing code. Haha!
(Note: The above is an adaptation of the real conversation. APIJSON does not require interfaces, documents, and features compatible with older versions of the client only for GET and HEAD requests. Fortunately, most requests are GET requests)
APIJSON, let interfaces and documents go to hell!
Source code and documentation (Welcome to Star, welcome to Fork)
Github: https://github.com/TommyLemon/APIJSON
Open source China: http://git.oschina.net/Lemon19950301/APIJSON
Download trial (test server address:139.196.140.118: 8080)
APIJSONClientApp.apk