//jsonClient 客户端 import JSON; import inet.http; import inet.httpStatusCode; import string.encoding; namespace web.rpc; class jsonClient { ctor( url, ... ) { this.rpc = { url = url; http = ..inet.http(...); charset = "UTF-8"; version = "2.0"; endCallback = {}; varargs = true; setAuth = function(user,pwd){ owner.http.username = user; owner.http.password = pwd; }; postData = function( reqData ){ owner.lastResponseData = null; owner.lastStatusCode = null; var jsonData = ..JSON.stringify(reqData); if( owner.afterJsonStringify ) { jsonData = owner.afterJsonStringify(jsonData); if(!jsonData) return; }; var ret,errMsg,errCode = owner.http.down( owner.url,jsonData ,'Content-Type:application/json-rpc',,"application/json,text/json,*/*","POST" ); owner.lastStatusCode = owner.http.statusCode; if( reqData.id === null){ if(!owner.transaction)return; } var utf8 = ("UTF-8" == owner.charset); var codepage = !utf8 ? charset2Codepage(this.charset):0; if( ret ) { if(!utf8)ret = ..string.fromto( ret,codepage,65001); owner.lastResponseData = ret; if( ret == "" ) return; if( owner.beforeJsonParse ) { ret = owner.beforeJsonParse(ret); if(!ret) return; } var data = ..JSON.parse(ret); if(owner.transaction){ if(#data){ for(i=1;#data;1){ if(data[i].id){ var cb = owner.endCallback[data[i].id]; if(cb){ owner.endCallback[data[i].id] = null; if( cb.end ){ cb.end( data[i].result,data[i].error ) } }; } } } return true; } if(data[["error"]]){ var err = ..JSON.stringify(data[["error"]],true) ; if(owner.onError) owner.onError(err,data[["error"]]); return data,err; } return data; } else { if( type(errMsg) == type.string ){ if(!utf8)errMsg = ..string.fromto( errMsg,codepage,65001); } owner.lastResponseData = errMsg; if(owner.onError) owner.onError(tostring(errMsg)); return null,errMsg,errCode; } }; notify = function(method,...){ var reqData = { jsonrpc = owner.version; method = method; } if( owner.varargs ){ reqData.params = [...]; } else { reqData.params = ...; } if( owner[["beforeRequest"]] ){ reqData = owner.beforeRequest(reqData); }; if( owner[["transaction"]] ){ ..table.push(owner.transaction,reqData); return; }; var _,errMsg,errCode = owner.postData( reqData ); if(!errMsg) return true; return null,errMsg,errCode }; xcall = function(method,...){ var reqData = { jsonrpc = owner.version; method = method; id = method; } if( owner.varargs ){ reqData.params = [...]; } else { reqData.params = ...; } if( owner[["beforeRequest"]] ){ reqData = owner.beforeRequest(reqData); }; if( owner[["transaction"]] ){ var cb = {}; cb.method = method; reqData.id = ..table.push(owner.endCallback,cb); ..table.push(owner.transaction,reqData); return cb; } return owner.postData( reqData ); }; beginTrans = function(){ owner.transaction = {}; owner.endCallback = {}; }; commitTrans = function(){ return owner.postData(owner.transaction); }; rollbackTrans = function(){ owner.transaction = null; owner.endCallback = null; }; lastStatusMessage = function(){ if(owner.lastStatusCode)return ..inet.httpStatusCode[owner.lastStatusCode],owner.lastStatusCode; }; lastResponse = function(){ var s = owner[["lastResponseData"]]; if( ..console && _STUDIO_INVOKED ) ..console.log(s) else ..io.print(s); return s; }; close = function(){ owner.http.close(); }; }; } @_meta } namespace jsonClient { _httpStatusCode = ..inet.httpStatusCode; _meta = { _get = function(k){ if( type(k) == type.string ){ if( owner[["method"]] ){ k = owner[["method"]] + "." + k; } owner[[k]] = { rpc = owner[["rpc"]]; method = k; @_meta; } return owner[[k]]; } } _call = function( ... ){ return owner.rpc.xcall( owner.method, ...) ; } } charset2Codepage = ..string.encoding.getCodepage; } /**intellisense() web.rpc.jsonClient = JSON-RPC 2.0客户端\n[关于JSON-RPC 2.0](http://www.jsonrpc.org/specification) web.rpc.jsonClient("__/*服务端网址*/") = 创建REST客户端 web.rpc.jsonClient() = !web_rpcJsonClientObject. !web_rpcJsonClientObject.? = 远程对象名或远程方法名字。\n远程函数调用成功则第一个返回值为表对象,\n└── 该对象的 result 字段为返回值,error 为错误对象,两个字段不会同时存在。\n└── error 对象的 message 为错误信息,code 为错误代码,data 为错误附加数据。\n└── 如果存在 error 对象,则第 2 个返回值为 error 对象的 JSON 文本格式。\n调用失败第一个返回值为 null ,第二个返回值为错误信息\n\n如果调用 rpc.beginTrans 函数开始一个批量调用,\n则返回一个可以指定 end 成员函数的对象,服务器应答时自动回调此 end 函数\n!web_rpcJsonClientObject. !web_rpcJsonClientObject.rpc = RPC 本地客户端对象\n所以本地函数放在这个对象里面\n用户不得添加、删除、修改此对象的任何成员 end intellisense**/ /**intellisense(!web_rpcJsonClientObject.rpc) setAuth(.("用户名","密码") = 设置HTTP登录用户名,密码 lastStatusCode = 获取最近一次请求返回的HTTP状态码\n100 ~ 101 为信息提示\n200 ~ 206 表示请求成功\n300 ~ 305 表示重定向\n400 ~ 415 表求客户端请求出错\n500 ~ 505 表示服务端错误 lastStatusMessage() = 获取最近返回的HTTP状态码文本描述\n第二个返回值为状态码 lastResponse() = 获取最后一次服务器返回的原始数据,\n如果控制台已打开或在开发环境中导入 console 库则在控制台输出数据\n下载文件时该值为空 http = 用于执行http请求的inet.http对象\n!inet_http. close() = 关闭RPC客户端并释放资源 beginTrans() = 开始批量调用\n批量调用时单次调用的返回值是一个表\n可对返回值指定 end 成员函数,\nend 函数会在 commitTrans 提交批量调用成功后被触发 commitTrans() = 完成批量调用 rollbackTrans() = 撤消尚未提交的批量调用 notify(.("函数名",参数) = 发送通知,服务器不返回值,\n如果 varargs 属性指定为 true 时将不定个数参数合为数组,\nvarargs 为 false 时直接发送参数\n\n成功返回 true,失败返回 null,错误信息 xcall(.("函数名",参数) = 调用远程函数。\n如果 varargs 属性指定为 true 时将不定个数参数合为数组,\nvarargs 为 false 时直接发送参数\n\n远程函数调用成功则第一个返回值为表对象,\n└── 该对象的 result 字段为返回值,error 为错误对象,两个字段不会同时存在。\n└── error 对象的 message 为错误信息,code 为错误代码,data 为错误附加数据。\n└── 如果存在 error 对象,则第 2 个返回值为 error 对象的 JSON 文本格式。\n调用失败第一个返回值为 null ,第二个返回值为错误信息\n\n如果调用 rpc.beginTrans 函数开始一个批量调用,\n则返回一个可以指定 end 成员函数的对象,服务器应答时自动回调此 end 函数 charset = 获取或修改编码,此值必须为大写,默认为"UTF-8",\n因为JSON规定了Unicode编码方案,所以不建议也不应该修改这个值 version = 值为JSON-RPC协议版本:"2.0"\n不应该修改这个值 varargs = 默认值为true,\n值为true时将不定个数的参数放入数组发送给服务端\n值为false时直接将单个参数发送给服务端\n\nJSON-RPC 2.0一个会制造混乱的地方:\n如果params是一个数组,并没有规定是展开为一个参数,还是作为一个数组参数。\n\n目前aardio的RPC服务端会负责展开数组作为多个参数,\n但客户端需要在这里手动设置 beforeRequest(请求数据) = @.beforeRequest = function(reqData){ __/*此回调事件在发送请求前触发\nreqData.params是即将发送的参数*/ return reqData; } beforeJsonParse = @.beforeJsonParse = function(jsonData){ __/*可以在这里解密服务端数据,返回null中止本次调用*/ return jsonData; } afterJsonStringify = @.afterJsonStringify = function(jsonData){ __/*可以在这里加密客户端JSON,返回null中止本次调用*/ return jsonData; } onError = function(errMessage,rpcError){ __/*出错时触发此函数,errMessage 为错误信息。\n如果服务端按 jsonrpc 协议返回 error 对象,\n则 rpcError 有值,包含 messge,code 字段。*/ } end intellisense**/