//client 通用 import inet.http; import inet.httpStatusCode; import string.encoding; import web.multipartFormData; import inet.tls; namespace web.rest; class client{ ctor(...) { if(type(...)==="table"){ var cfg = ...; this._http = ..inet.http(cfg.userAgent,cfg.proxy,cfg.proxyBypass,cfg.httpFlags); this.config = cfg; } else { this._http = ..inet.http(...) } this._http.disableCache(); this._userAgent = this._http._userAgent; this.stringifyRequestParameters = ..inet.url.stringifyParameters; this.parseResponseResult = parseResponseResult; this.charset = "UTF-8"; this.contentType = "application/x-www-form-urlencoded"; this.defaultContentType = "application/x-www-form-urlencoded"; this.encodeKey = ..inet.url.encodeMbcs; var fileSend,fileReceive; }; setHeaders = function(headers){ if( type(this.addHeaders) != type.table ) this.addHeaders = null; this.addHeaders = ..table.assign(this.addHeaders,headers); }; requestEx = function(req){ if(!req) error("参数必须指定 lastRequestInfo",2); return this.request(req.url,req.params,req.httpMethod,req.urlParams,req.receiveCallback); }; request = function(url,params,httpMethod,urlParams,receiveCallback,...){ if(owner.__clientMethod){ var send = this[owner.__clientMethod]; var ok,err = send(params,urlParams,receiveCallback,...); urlParams,receiveCallback = null,null; if(!ok) return null,err; if(send == this.receiveFile) return owner; } httpMethod = !httpMethod ? owner.__httpMethod : httpMethod; this.lastRequestInfo = { url = url, params = type(params) =="table" ? ..table.assign({},params) : params, httpMethod = httpMethod , urlParams = urlParams, receiveCallback = receiveCallback } this.lastRequestUrl = url; this.lastRequestMethod = httpMethod; var utf8 = ..string.cmp("UTF-8",this.charset ) == 0; var codepage = charset2Codepage(this.charset):0; if( this.extraUrlParameters ) { this.lastRequestUrl = ..inet.url.appendExtraInfo( this.lastRequestUrl, ..inet.url.stringifyParameters(this.extraUrlParameters,codepage) ); }; if(urlParams){ if(type(urlParams)=="function") receiveCallback = urlParams; else { this.lastRequestUrl = ..inet.url.appendExtraInfo( this.lastRequestUrl, ..inet.url.stringifyParameters(urlParams,codepage) ); } } if( this.extraParameters ) { if( params === null ){ params = ..table.mix( params,this.extraParameters ); } elseif( type(params)==type.table ){ for(k,v in this.extraParameters){ if( params[k] === null ){ params[k] = v; continue; } if((type(v)==="table") && (type(params[k])==="table") ){ ..table.mix(params[k],v); } } } }; if( this.beforeStringifyRequestParameters ){ params = this.beforeStringifyRequestParameters(params,codepage); } var tParams = params; if(params && (type(params)!=type.string) ){ if( httpMethod == "GET" ){ params = ..inet.url.stringifyParameters(params,codepage); } else{ params = this.stringifyRequestParameters( params,codepage); } }; if( this.afterStringifyRequestParameters ){ params = this.afterStringifyRequestParameters(params,codepage); } var contentType = this.contentType || this.defaultContentType; var headers; if(this.beforeRequestHeaders){ headers = this.beforeRequestHeaders(tParams,params,this.lastRequestUrl,httpMethod,contentType); } this.lastResponseData = null; this.lastStatusCode = null; this.lastResponseErrorData = null; this._http.beforeSend = this.beforeSend; this._http.afterSend = this.afterSend; this._http.addHeaders = this.addHeaders; if(receiveCallback){ if( fileReceive ){ error("调用 receiveFile 函数后的下一个请求不能指定 receiveCallback 回调参数",2) } else { this._http.parseStreamData = this.parseResponseResult; } this._http.onReceiveBegin = null; this._http.onReceive = receiveCallback; } else { this._http.parseStreamData = null; } headers = ..web.joinHeaders('Content-Type:' + contentType,headers); if(this["(TOKEN)"]) headers = ..web.joinHeaders('Authorization:' + this["(TOKEN)"],headers); var ret,errMsg,errCode = this._http.down(this.lastRequestUrl,params ,headers,this.referer,this.acceptType,this.lastRequestMethod,owner[["__httpFlags"]]); this.contentType = this.defaultContentType; this.lastResponseContentType = this._http.responseContentType; if(receiveCallback){ this._http.onReceive = null; } //关闭上传下载文件 if( fileSend ){ fileSend.close(); //兼容 io.file,web.multipartFormData fileSend = null; this._http.onSendBegin = null; this._http.onSend = null; } if( fileReceive ){ fileReceive.close(); fileReceive = null; this._http.onReceiveBegin = null; this._http.onReceive = null; } this.lastStatusCode = this._http.statusCode; if( ret ) { if( this._http.responseContentType ){ var charset = ..string.match(this._http.responseContentType,";\s*charset\s*=\s*([^;\s]+)"); if( charset ) { this.charset = charset; utf8 = ..string.cmp("UTF-8",this.charset ) == 0; } } //HEAD 请求 if(!type.isString(ret)){ return ret; } if( ret[1] == 0xEF && ret[2] == 0xBB && ret[3] == 0xBF ){ ret = ..string.right(ret,-4) ; utf8 = true; }; if( !utf8 ){ var codepage = charset2Codepage(this.charset); if( codepage !== null ) ret = ..string.fromto( ret,codepage,65001); } this.lastResponseData = ret; if( this._http.lastReadErrCode ) { return null,..lasterr(this._http.lastReadErrCode); } if( owner[["__pattern"]] && ret ){ var tp = type( owner.__pattern); if( tp == "table"){ return ..string.map(ret,owner.__pattern); } elseif( tp == "function"){ return owner.__pattern(ret); } ret = ..string.match(ret,owner.__pattern); if(!ret) return null,"服务器返回的数据中未找到匹配的部分" } if(!this.checkResponseResult){ return this.parseResponseResult(ret); } else { return this.checkResponseResult( this.parseResponseResult(ret) ); } } else { if(this.lastStatusCode==401 && this.onAuthenticate){ this._http.head(this.lastRequestUrl); var authHeader = this._http.readHeader("WWW-Authenticate") if(authHeader){ var req = this.lastRequestInfo; if(this.onAuthenticate(authHeader)){ return this.requestEx(req) } } } if( type(errMsg) == type.string ) { if(this.lastStatusCode){ if( !utf8 ) errMsg = ..string.fromto( errMsg,codepage,65001); elseif( !..string.len(errMsg)) errMsg = ..string.fromto(errMsg,0,65001); this.lastResponseData = errMsg; this.lastResponseErrorData = errMsg; } } return null,errMsg,errCode; } }; headPost = function(url,params){ return this.request(url,params,"HEADPOST"); }; headGet = function(url,params){ return this.request(url,params,"HEADGET"); }; head = function(url,params){ return this.request(url,params,"HEAD"); }; get = function(url,params,receiveCallback){ return this.request(url,params,"GET",,receiveCallback); }; post = function(url,params,receiveCallback){ return this.request(url,params,"POST",,receiveCallback); }; put = function(url,params){ return this.request(url,params,"PUT"); }; delete = function(url,params){ return this.request(url,params,"DELETE"); }; patch = function(url,params){ return this.request(url,params,"PATCH"); }; ok = function(){ var statusCode = this._http.statusCode; return statusCode ? ( statusCode >= 200 ) ? statusCode < 300 }; lastStatusMessage = function(){ if(this.lastStatusCode)return ..inet.httpStatusCode[this.lastStatusCode],this.lastStatusCode; }; lastResponse = function(){ var s = this.lastResponseData; if( ..console && _STUDIO_INVOKED ) ..console.log(s) else ..io.print(s); return s; }; lastResponseString = function(){ return this.lastResponseData; }; lastResponseObject = function(){ if( this.lastResponseDataObject ) { return this.lastResponseDataObject; } if(this.lastResponseData){ var e = this.parseResponseResult(this.lastResponseData); if( field ) return e[[field]]; return e; } }; lastResponseError = function(field){ if(this.lastResponseErrorData){ var e; try{ e = this.parseResponseResult(this.lastResponseErrorData); if( field ) return e[[field]]; } return e; } }; lastResponseHeaders = function(name,index){ return this._http.readHeader(name,index); }; lastResponseHeaderList = function(name){ return this._http.readHeaderList(name); }; api = function(urlTemplate,httpMethod="POST",pattern,httpFlags ){ if(!urlTemplate){ urlTemplate = this.defaultUrlTemplate; if(!urlTemplate) error("参数@1错误",2); } elseif(this.defaultUrlTemplate && (urlTemplate[1]==='/'# || urlTemplate[1]==='.'#) ){ urlTemplate = ..inet.url.joinpath(this.defaultUrlTemplate,urlTemplate) } var urlRequest = urlTemplate; urlRequest = ..string.replace(urlRequest,"/\{\s*\.\.\.\s*\}","",1); urlRequest = ..string.replace(urlRequest ,"%{}", ""); return { __httpFlags = httpFlags; __httpMethod = ..string.upper( httpMethod ); __urlTemplate = urlTemplate; __urlReal = urlRequest; __request = this.request; __encodeKey = this.encodeKey; __pattern = pattern; @_meta; } }; declareApi = function(...){ return this.api(...); }; sendMultipartForm = function(fields,stepProc,blockSize = 0x2000 ){ if(!fields) error("请指定上传表单字段参数",2); if(fileSend) fileSend.close(); fileSend = ..web.multipartFormData(); if( ..string.cmp("UTF-8",this.charset ) != 0 ) fileSend.codePage = charset2Codepage(this.charset); for(k,v in fields){ fileSend.add(k,v); } var contentLength; this._http.onSendBegin = function(){ contentLength = fileSend.size(); if( stepProc ) stepProc(,0,contentLength,contentLength); return contentLength; } this._http.onSend = function(remainSize){ var str = fileSend.read(blockSize); if( stepProc ) stepProc(str,#str,contentLength,remainSize); return str; } this.contentType = fileSend.contentType(); return this; }; sendFile = function(path,stepProc,mime,blockSize = 0x2000 ){ if(!path) error("请指定路径参数",2); if(fileSend) fileSend.close(); var err; fileSend,err = ..io.file(path,"rb"); if(!fileSend) return null,err; var contentLength; this._http.onSendBegin = function(){ contentLength = fileSend.size(1); if( stepProc ) stepProc(,0,contentLength,contentLength); return contentLength; } this._http.onSend = function(remainSize){ var sendData = fileSend.read(blockSize); if( stepProc ) if( stepProc(sendData,#sendData,contentLength,remainSize) === false ) return; return sendData; } this.contentType = mime or ..fsys.mime.fromFile(path); return this; }; receiveFile = function(path,stepProc,saveDir){ if(!path) error("请指定路径参数",2); if(saveDir) path = ..io.joinpath(saveDir,path); if(fileReceive) fileReceive.close(); var err; fileReceive,err = ..io.file(path,"w+b"); if( !fileReceive ) { ..io.createDir( ..io.splitpath(path).dir ); fileReceive,err = ..io.file(path,"w+b"); if( !fileReceive ) return null,err; } this._http.onReceiveBegin = function(statusCode,contentLength){ if(stepProc) return stepProc(0,0,contentLength,statusCode); } this._http.onReceive = function(recvData,recvSize,contentLength){ fileReceive.write(recvData); if(stepProc) return stepProc(recvData,recvSize,contentLength); } return this; }; endRequest = function(){ this._http.endRequest(); }; notify = function(func,...){ var afterSend = this.afterSend; this.afterSend = function(){ if(afterSend) invoke(afterSend,this); this._http.endRequest(); } func(...); this.afterSend = afterSend; }; getUserAgent = function(){ return this._userAgent; }; setAuth = function(user,pwd){ this._http.username = user; this._http.password = pwd; this["(TOKEN)"] = null; }; setAuthToken = function(token,t){ if(token && ..string.match(token,"^https?\:") ){ token = this._http.get(token); }; if(!token) { this["(TOKEN)"] = null; return false; } this._http.username = null; this._http.password = null; this["(TOKEN)"] = (t:"Bearer") + " " + token; return true; }; setCookie = function(...){ return ..inet.setCookie(...); }; setTimeouts = function(...){ this._http.setTimeouts(...); }; close = function(){ this._http.close(); }; } namespace client { charset2Codepage = ..string.encoding.getCodepage; parseResponseResult = function(ret,streamChunk){ return ret; } _httpStatusCode = ..inet.httpStatusCode; _defaultMethod = { head = "HEAD";get = "GET"; post = "POST"; put = "PUT"; delete = "DELETE"; patch = "PATCH"; headHead = "HEAD";getGet = "GET"; postPost = "POST"; putPut = "PUT"; deleteDelete = "DELETE"; patchPatch = "PATCH"; sendMultipartForm = "";sendFile = "";receiveFile = "";getUrl = ""; }; _defaultMethodUrl ={ headHead = "/head";getGet = "/get"; postPost = "/post"; putPut = "/put"; deleteDelete = "/delete"; patchPatch = "/patch"; }; _meta = { _get = function( k ){ var defMethod = _defaultMethod[k]; if( defMethod ){ var api = { __urlReal = owner[["__urlReal"]]; __urlTemplate = owner[["__urlTemplate"]]; __httpFlags = owner[["__httpFlags"]]; __request = owner[["__request"]]; __pattern = owner[["__pattern"]]; __encodeKey = owner[["__encodeKey"]]; @_meta; } if(#defMethod){ if(owner[["__httpMethod"]]!="HEAD"){ api.__httpMethod = defMethod; } else { api.__httpMethod = "HEAD" + defMethod; } if(#k > #defMethod){ api = api[ _defaultMethodUrl[k] ] } } else { if(k==="getUrl") return lambda() owner.__urlReal; api.__httpMethod = owner[["__httpMethod"]]; api.__clientMethod = k; } owner[[k]] = api; return api; } else { var tk = type(k); var urlTemplate,count; if( tk == "string" && k[1]=='_'# ) return; elseif( tk == "table") { var encodeKey = owner.__encodeKey; urlTemplate,count = ..string.replace(owner[["__urlTemplate"]],"\{\s*(/?)(\w+)\s*\}",lambda(slash,t)k[t]?(slash++encodeKey(k[t])):""); } else { k = tostring(k); if(!k) return; var repk = ..string.replace(k,"@\","\\"); repk = owner.__encodeKey(repk); urlTemplate,count = ..string.replace(owner[["__urlTemplate"]],"\{(/?)\s*\w+\s*\}", #repk ? "\1"+repk : "",1); if(!count){ urlTemplate,count = ..string.replace(owner[["__urlTemplate"]],"\{\s*\.\.\.\s*\}", repk + "/{...}",1); if( !count ){ urlTemplate = owner[["__urlTemplate"]] if( urlTemplate[#urlTemplate] != '/'# && (k[1] != '/'#) ){ urlTemplate = urlTemplate + "/"; } urlTemplate = urlTemplate ++ repk; } } } var urlRequest = urlTemplate; urlRequest = ..string.replace(urlRequest,"/\{\s*\.\.\.\s*\}","",1); urlRequest = ..string.replace(urlRequest ,"%{}", ""); var api = { __urlReal = urlRequest; __urlTemplate = urlTemplate; __httpFlags = owner[["__httpFlags"]]; __httpMethod = owner[["__httpMethod"]]; __request = owner[["__request"]]; __pattern = owner[["__pattern"]]; __encodeKey = owner[["__encodeKey"]]; @_meta; } owner[[k]] = api; return api; } }; _call = function(params,urlParams,receiveCallback,...){ return owner.__request(owner.__urlReal,params,,urlParams,receiveCallback,...) ; }; _keys = {}; _type = "object"; } } //@guide [必读:web.rest 使用指南](doc://library-guide/std/web/rest/client.html) /**intellisense() web.rest.client = REST客户端 web.rest.client() = !web_restClient. web.rest.client( = 创建 REST 客户端。 web.rest.client(.(userAgent,proxy,proxyBypass,httpFlags) = 创建 REST 客户端,所有参数可选。\nuserAgent 参数用于自定义 User-Agent 请求标头,用于服务器识别请求程序特征。\nHTTP 代理服务器请指定为 "代理服务器地址:端口" 格式,\nproxy 参数指定代理,proxyBypass 参数指定绕过代理的主机。\n└── [代理参数格式说明](doc://library-guide/std/inet/proxy.md)\nhttpFlags 可选用一个数值指定 HTTP 连接选项,一般不需要指定。 \n\n可选传入一个表指定以上参数以及其他 API 自定义设置。\n如果传入表参数则存为对象的 config 属性。 web.rest.client(.(config) = 创建 REST 客户端。\n可选用一个表参数指定 API 自定义的配置项。\n参数存为对象的 config 属性。\n\n参数 config 的 userAgent,proxy,proxyBypass,flags 字段将作为 web.rest.client 构造参数。 !web_restClient.config = 自定义的 API 配置表。\n默认指向创建对象时指定的表参数。\n如果构造对象未指定表参数,此配置默认为 null 值。 !web_restClient.stringifyRequestParameters(提交参数表,代码页) = @.stringifyRequestParameters = function(params,codepage){ __/*参数是表对象时调用此函数转换为字符串\n此函数一般由库文件定义,一般调用不应修改此函数\n注意返回的文本格式应与 owner.contentType 一致\n在该函数中可获取或修改对象的 lastRequestUrl, lastRequestMethod 属性*/ return params; } !web_restClient.parseResponseResult(响应数据,单个流式数据块) = @.parseResponseResult = function(resp,streamChunk){ __/*用于解析服务器应答数据。\n此函数一般由库文件定义,一般调用不应修改此函数。\nresp 为服务器应答字符串,\nstreamChunk 指示是否在 SSE 或 ndjson 流回调函数中解析单个块*/ return resp; } !web_restClient.beforeStringifyRequestParameters(提交参数,代码页) = @.beforeStringifyRequestParameters = function(params,codepage){ __/*可以在该函数中修改提交提交参数表,或在此添加默认参数\n此函数在stringifyRequestParameters之前调用\n参数可能是字符串或者表*/ return params; } !web_restClient.beforeRequestHeaders(提交参数,提交数据,请求网址,请求方法,请求类型) = @.beforeRequestHeaders = function(params,payload,url,httpMethod,contentType){ __/*每次请求前触发此函数。\nparams 参数为本次请求的提交参数,\npayload 为转换为字符串格式的提交数据\nurl 参数为本次请求网址。\nhttpMethod 参数为 HTTP 方法(大写)。\ncontentType 参数为请求内容类型。\n可选以字符串、数组、键值等 web.joinHeaders 函数支持的格式返回 HTTP 头。\n*/ return {}; } !web_restClient.afterStringifyRequestParameters(提交参数,代码页) = @.afterStringifyRequestParameters = function(params,codepage){ __/*此时参数 params 已转换为字符串并准备提交到服务器。\ncodepage 参数为请求数据使用的代码页。\n必须用第一个返回值返回新的 params 。*/ return params; } !web_restClient.encodeKey = 自定义替换 API 网址变量值使用的编码函数,\n此函数接收一个资源名参数,返回编码后的资源名,\n必须在定义 API 对象之前指定,\n默认已指定为 inet.url.encodeMbcs 函数 !web_restClient.defaultUrlTemplate = 默认API网址模板 !web_restClient.extraParameters = 指定附加到所有请求参数表中的默认附加参数表,此属性必须是 null 或者表对象。\n调用 HTTP API 接口时,请求参数指定表对象时或为 null 才会附加 extraParameters 属性指定的参数。\n不会用附加参数表覆盖请求参数表中已存在的字段。\n如果请求参数表与附加参数表中都已存在同名字段,并且值也是一个表时会合并子表的值。\n仅合并第一层子表,合并子表时也不会覆盖已存在的值。 !web_restClient.extraUrlParameters = 指定附加到所有请求 URL 的默认参数。\n该值可以是一个表或字符串。\n表参数使用 inet.url.stringifyParameters 转换为字符串。\n表中的值如果是函数则每次请求都调用该函数取值 !web_restClient.acceptType = 期望返回的MIME内容类型,多个类型用逗号分隔 !web_restClient.contentType = 写入HTTP头ContentType属性的内容类型\n设为null空值,或每次请求完成将自动重置为defaultContentType !web_restClient.defaultContentType = 默认内容类型 !web_restClient.setHeaders( = 设置所有请求默认添加的HTTP头 !web_restClient.setHeaders(.(headers) = 参数 @headers 必须指定一个表中,\n用该表中的键值对更新 addHeaders 属性中的键值\n如果addHeaders的原属性值不是一个表,则先清空该属性 !web_restClient.addHeaders = 替换所有请求默认添加的HTTP头\n请求结束时不会清空此属性\n该值可以是一个字符串,也可以是键值对组成的table对象 !web_restClient.referer = 引用页地址。\n如果此属性指定了一个值,则每次请求都会使用该引用页。\n如果不指定,每次请求都会自动设置上次请求的网址为引用页。\n这个属性不像 inet.http 对象的 referer 属性那样每次请求结束都会清空。 !web_restClient.charset = 指定服务端使用的编码,默认为"UTF-8",\n服务端HTTP响应头里如果显式指定了编码,会自动修改此属性\n对于服务端返回的数据,会根据charset自动转换为aardio默认的UTF8编码\n对于请求参数,如果使用JSON编码统一转换为Unicode编码格式,不需要考虑编码转换,\n如果使用UrlEncode编码与浏览器的规则一致,根据charset转换为对应编码后提交,\n开发服务端接口应统一使用UTF8编码,可避免任何乱码问题 !web_restClient.getUserAgent() = 返回HTTP客户端请求HTTP头中用户代理头的值,\n该值可在创建HTTP客户端的构造参数中指定 !web_restClient.setAuth( = 设置登录名,密码,或者设置 API Key 与 Secret Key !web_restClient.setAuth(.("登录名","密码") = 设置登录名,密码,\n默认用于HTTP登录认证\n,\n支持 Basic , Digest 认证\n\n此函数会清空 setAuthToken 设置的认证令牌(token) !web_restClient.setAuth(.("API Key","Secret Key") = 设置 API Key,Secret Key。\n此函数会清空 setAuthToken 设置的认证令牌(token) !web_restClient.setAuthToken( = 设置认证令牌(token) !web_restClient.setAuthToken(.(token,tokenType) = 设置认证令牌(token)。\n@token 可指定令牌,也可以指定返回令牌的网址。\n@tokenType 为可选参数,默认为"Bearer"。\n如果指定了令牌,则会添加请求头 "Authorization:Bearer 令牌"。\n@tokenType 也可以指定为空字符串。\n\n此函数会清空 setAuth 设置的用户名与密码 !web_restClient.setCookie( = 调用 inet.setCookie 设置 Cookies 。\nweb.rest,inet.http,web.form 等共享 Cookies 设置。\n\n此函数用法与 inet.setCookie 函数完全相同。\n这里仅列出几种常用的参数用法,\n其他用法请查看 inet.setCookie 函数说明 !web_restClient.setCookie(.("网址","值","键",过期时间或天数) = 设定指定网址的 Cookie。\n第一个参数指定 Cookie 生效的网址。\n如果不指定值则删除删除指定名字的会话 cookie 与持久 Cookie,\n如果指定名字与值则设置 Cookie,如果不指定过期时间则仅设置会话 Cookie,\n否则设置为持久 Cookie。\n可选参数 @4 使用 time 对象指定过期时间,或用数值指定过期天数 !web_restClient.setCookie(.(webView) = 传入 web.view 可直接获取浏览器控件的 Cookies\n作为 inet.setCookie 的参数,设置为 inet.http 可用的 Cookies 。\nweb.form,web.rest,inet.http 共享此 Cookies 设置 !web_restClient.? = web.rest.client 或继承自 web.rest.client 的对象的其他成员函数或属性。\n请查看相关库函数文档或源码 !web_restClient.ok() = 最后一次请求是否成功\n服务器应答并且状态码为2XX该函数返回真 !web_restClient.lastStatusCode = 获取最近一次请求返回的HTTP状态码\n100 ~ 101 为信息提示\n200 ~ 206 表示请求成功\n300 ~ 305 表示重定向\n400 ~ 415 表求客户端请求出错\n500 ~ 505 表示服务端错误 !web_restClient.lastStatusMessage() = 获取最近返回的HTTP状态码文本描述\n第二个返回值为状态码 !web_restClient.lastResponseContentType = 服务器响应头 Content-Type 的值 !web_restClient.lastResponseHeaders( = 获取返回的 HTTP 响应头 !web_restClient.lastResponseHeaders(.() = 获取返回的 HTTP 响应头。\n如果调用 head, headGet, headPost 等函数。\n或调用 或 API 对象的 head.get,head.post 等函数,\n则在请求成功后可调用此函数读取 HTTP 头 !web_restClient.lastResponseHeaders(.("Content-Length:") = 获取单个指定的 HTTP 服务器响应头,返回字符串。\n参数指定HTTP头名字或部分名字,忽略大小写,忽略冒号以后部分。\n如果调用 head, headGet, headPost 等函数。\n或调用 或 API 对象的 head.get,head.post 等函数,\n则在请求成功后可调用此函数读取 HTTP 头 !web_restClient.lastResponseHeaderList("__") = 获取所有指定字符串作为开始段的 HTTP 头。\n忽略大小写,不忽略冒号以后部分。\n不指定参数则读取全部响应头。\n返回键值对组成的表对象,键名转为小写,每个值都是一个数组。\n如果调用 head, headGet, headPost 等函数。\n或调用 或 API 对象的 head.get,head.post 等函数,\n则在请求成功后可调用此函数读取 HTTP 头 !web_restClient.lastResponse() = 获取最后一次服务器返回的数据,\n如果控制台已打开或在开发环境中导入 console 库则在控制台输出数据\n下载文件时该值为空 !web_restClient.lastResponseObject() = 获取最后一次服务器返回的对象(已将响应文本解析为对象),\n请求失败,或者下载文件时此属性值为空。\n如果是 SSE 流式调用,默认无返回值。\n web.rest.aiChat 则在流式调用时会返回最后一次接受的包含 token 计数的对象。 !web_restClient.lastResponseString() = 获取最后一次服务器返回的原始数据,\n请求失败,或者下载文件时此属性值为空 !web_restClient.lastResponseError() = 返回服务器最后一次返回的错误响应,并转换为错误对象。\n与调用 API 时转换响应数据一样,支持相同的服务器响应格式 。\n如果错误来自本地(lastStatusCode 属性为 null)则此函数返回 null 。\n如果最后一次发生请求成功,则此函数返回 null 。\n\n如果在参数 @1 中指定返回字段,且错误对象包含该字段则使用直接下标获取并返回字段值。\n获取错误对象失败、或格式解析失败、获取字段失败都会返回 null 而非抛出异常 !web_restClient.lastRequestUrl = 获取最后一次请求的 URL。\n允许的 beforeRequestHeaders 事件中修改此属性以改变请求地址。 !web_restClient.lastRequestMethod = 最后一次请示使用的HTTP方法 !web_restClient.lastRequestInfo = 最后一次请示使用的所有参数信息 !web_restClient.setTimeouts(.(连接超时,请求超时,接收超时) = 设置超时,以亳秒为单位(1秒为1000毫秒)。 !web_restClient.strictParsing = 对于 JSON 客户端,如果此属性设为 true:\n且服务器响应头未声明 JSON 类型,返回数据第一个字符不是 { 也不是 [,\n则直接返回包含服务器响应数据的字符串。\n\n此属性值默认为 null !web_restClient._http = inet.http客户端,用于执行 http 请求\n!inet_http. !web_restClient.close() = 关闭对象释放资源 !web_restClient.beforeSend = @.beforeSend = function(){ __/*已准备向服务器发送数据触发此回调函数*/ } !web_restClient.afterSend = @.afterSend = function(statusCode,contentLength){ __/*向服务器发送数据结束触发此回调函数*/ } !web_restClient.checkResponseResult(result) = @.checkResponseResult = function(result){ if(result[["errcode"]]==40014){ var lastRequestInfo = http.lastRequestInfo; __/*此函数在成功解析服务器响数数据后调用\n可在此函数中检查token是否过期并重新发送请求*/ return http.requestEx(lastRequestInfo); } return result; } !web_restClient.onAuthenticate = function(authHeader){ __/*请求遇到401错误触发此函数,\nauthHeader为服务器响应HTT头WWW-Authenticate的值\n登录成功请返回true以重新发送上次失败的请求*/ } !web_restClient.endRequest() = 可用于afterSend事件提前结束当前请求 !web_restClient.notify(回调函数) = @.notify(\n function(){\n __/*在此回调函数中所有请求在发送数据后立即关闭连接*/\n } \n) !web_restClient.sendMultipartForm(上传字段表,进度回调函数) = @.sendMultipartForm( {\n file = "@__/*上传文件路径字前必须添加@字符,\n上传表单可添加多个字段,\n上传进度回调函数以及其他参数都可以省略*/"\n },function(sendData,sendSize,contentLength,remainSize){\n \n }\n); !web_restClient.sendFile("上传文件路径",进度回调函数) = @.sendFile( "上传文件路径" \n ,function(sendData,sendSize,contentLength,remainSize){\n __/*上传进度回调函数以及其他参数都可以省略,\n如果打开文件失败,sendFile 函数返回 null 与错误信息,\n否则返回响应数据*/\n }\n); !web_restClient.receiveFile( = 配置下次下载文件的参数,\n此配置在后续用 get,post 等函数提交请求后自动清空\n\n此函数成功返回对象自身,创建文件失败返回 null,错误信息 !web_restClient.receiveFile(.(savePath,callback,saveDir) = 配置下次下载文件的参数,\n此配置在后续用 get,post 等函数提交请求后自动清空\n此函数成功则返回对象自身\n\n@savePath 参数指定下载文件路径,\n可选用 @saveDir 指定下载根目录,指定此参数则参数 @1 应为相对路径。 !web_restClient.sendMultipartForm() = !web_restClient. !web_restClient.sendFile() = !web_restClient. !web_restClient.receiveFile() = !web_restClient. !web_restClient.requestEx(.(lastRequstInfo) = 用于重新发送请求,\n参数必须指定当前对象的lastRequestInfo属性 !web_restClient.request( = 发送请求,\n该函数不会解析URL中的模板参数 !web_restClient.request(.(网址,参数表,"HTTP动词",URL参数表) = 除参数@1以外,其他所有参数都可以省略,\n省略参数@3则使用默认的调用方法\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.headGet(.(网址,参数表) = 使用 GET 方法提交请求,但仅获取 HTTP 头。\n如果该函数返回非 null 值为成功,请使用 lastResponseHeaders 获取应答HTTP头\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.headPost(.(网址,参数表) = 使用 POST 方法提交请求,但仅获取 HTTP 头。\n如果该函数返回非 null 值为成功,请使用 lastResponseHeaders 获取应答HTTP头\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.head(.(网址,参数表) = 使用 HEAD 方法提交请求\n如果该函数返回非 null 值为成功,请使用 lastResponseHeaders 获取应答HTTP头。\n成功返回 true,失败返回 null,错误信息,错误代码 !web_restClient.get(.(网址,参数表,接收数据回调函数) = 使用该GET方法提交请求,获取资源\n请求参数将会自动转换为URL附加参数,\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n如果服务端返回内容类型为 text/event-stream,\n则接收数据回调函数的参数为解析服务端每次推送数据的表对象,\n否则回调参数依次为:当前响应数据,本次响应数据长度,响应数据总长度。\n回调函数如果返回 false 则停止接收数据。 !web_restClient.post(.(网址,参数表,接收数据回调函数) = 使用该POST方法提交请求,新增或修改资源\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n\n如果服务端返回内容类型为 text/event-stream,\n则接收数据回调函数的参数为解析服务端每次推送数据的表对象,\n否则回调参数依次为:当前响应数据,本次响应数据长度,响应数据总长度。\n回调函数如果返回 false 则停止接收数据。 !web_restClient.get(.(网址,参数表) = 使用该GET方法提交请求,获取资源\n请求参数将会自动转换为URL附加参数,\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.post(.(网址,参数表) = 使用该POST方法提交请求,新增或修改资源\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.delete(.(网址,参数表) = 使用该DELETE方法提交请求,删除资源\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.put(.(网址,参数表) = 使用该PUT方法提交请求,替换或更新资源\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.patch(.(网址,参数表) = 使用该PATCH方法提交请求,更新资源\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串\n成功返回数据,失败返回空值,错误信息,错误代码 !web_restClient.api( = 定义API对象,使用成员操作符或下标获取API对象的成员时将会自动转换为新的API对象 !web_restClient.api(.("网址模板","HTTP动词","模式串") = 网址模板可使用 {name}、{/name}、或 {...} 指定模板参数,\n大括号内的参数名可使用英文字符或数字,\n模板参数名首字符为斜杠(例如{/name})表示使用实参替换后保留斜杠,\n {...} 表示不定个数的模板参数,如使用多个实参替换则自动使用斜杠拼接,\n默认在网址模板尾部自动添加 {...}\n\n返回API对象使用下标指定模板实参,\n如果下标是字符串,按下标出现的前后顺序替换模板参数,\n如果下标是表对象,则表中的键值对用于替换与键同名的模板参数,\n\n可选用参数@2指定HTTP动词,默认值为"POST"\n\n参数@3如果为模式串,则使用模式匹配转换响应数据并返回。\n参数@3如果为函数,则调用该函数转换转换响应数据并返回。\n参数@3如果为数组,则作为string.map的模式参数@2转换响应数据并返回 !web_restClient.api(.("网址模板","HTTP动词","模式串",HTTP选项) = 可选在参数@4用 _INTERNET_FLAG_ 前缀常量自定义http请求选项\n其他参数用法同上 !web_restClient.api(.("/路径") = 如果客户端使用 defaultUrlTemplate 属性指定了默认网址模板。\n则这里的第一个参数可以省略,或者仅指定以 `/` 或 `.` 字符开始的路径。 !web_restClient.api() = !web_rest_api. !web_rest_api.? = 可输入任意 HTTP API 资源名\n返回一个 HTTP API 调用对象。\n对 API 调用对象可以使用成员操作符继续获取下级资源,\n也可以将其作为函数对象直接调用并自动发送 HTTP 请求。\n作为函数调用时成功返回解析后数据,失败返回 null,错误信息,错误代码。\n调用参数 @1 可选用表对象指定请求参数,也可直接用字符串指定请求参数。\n可选用参数 @2 指定 URL 参数表。\n可选用参数 @2 或 @3 指定 inet.http 对象的 onReceive 回调函数。\n指定 onReceive 回调函数则自动支持自动解析 SSE 流,兼容 ndjson 流。。\nonReceive 回调函数如果返回 false 则停止接收数据。\n!web_rest_api. !web_rest_api.head() = HEAD方法提交请求\n如果该函数返回非null值为成功,请使用lastResponseHeaders获取应答HTTP头\n请求参数可以指定表或字符串,如果是表请求前会转换为字符串 !web_rest_api.get(__) = 使用 HTTP 协议的 GET 方法提交 HTTP 请求。\n如果资源 URL 名称需要用到 get,请使用["/get"]替代以避免被识别为默认HTTP方法。\n成功返回解析后数据,失败返回 null,错误信息,错误代码。\n调用参数 @1 可选用表对象指定请求参数,也可直接用字符串指定请求参数。\n可选用参数 @2 指定额外的 URL 参数表。\n可选用参数 @2 或 @3 指定 inet.http 对象的 onReceive 回调函数。\n指定 onReceive 回调函数则自动支持自动解析 SSE 流,兼容 ndjson 流。。\nonReceive 回调函数如果返回 false 则停止接收数据。\n!web_rest_api. !web_rest_api.post(__) = 使用 HTTP 协议的 POST 方法提交 HTTP 请求。\n如果资源 URL 名称需要用到 post,请使用["/post"]替代以避免被识别为默认HTTP方法。\n成功返回解析后数据,失败返回 null,错误信息,错误代码。\n调用参数 @1 可选用表对象指定请求参数,也可直接用字符串指定请求参数。\n可选用参数 @2 指定 URL 参数表。\n可选用参数 @2 或 @3 指定 inet.http 对象的 onReceive 回调函数。\n指定 onReceive 回调函数则自动支持自动解析 SSE 流,兼容 ndjson 流。。\nonReceive 回调函数如果返回 false 则停止接收数据。\n!web_rest_api. !web_rest_api.delete(__) = DELETE方法提交请求,删除资源,\n请求参数@1可以指定表或字符串、缓冲区,如果是表请求前会转换为字符串\n可选用参数@2指定URL参数表,\n可选用参数@3指定inet.http对象的onReceive回调函数\n如果资源URL名称需要用到 delete,请使用["/delete"]替代以避免被识别为默认HTTP方法 !web_rest_api.put(__) = PUT方法提交请求,替换或更新资源,\n请求参数@1可以指定表或字符串、缓冲区,如果是表请求前会转换为字符串\n可选用参数@2指定URL参数表,\n可选用参数@3指定inet.http对象的onReceive回调函数 !web_rest_api.patch(__) = PATCH方法提交请求,更新资源,\n请求参数@1可以指定表或字符串、缓冲区,如果是表请求前会转换为字符串\n可选用参数@2指定URL参数表,\n可选用参数@3指定inet.http对象的onReceive回调函数 !web_rest_api.sendMultipartForm(上传字段表,进度回调函数,分块大小) = @.sendMultipartForm( {\n file = "@__/*上传文件路径字前必须添加@字符,\n上传表单可添加多个字段,\n上传进度回调函数以及其他参数都可以省略*/"\n },function(sendData,sendSize,contentLength,remainSize){\n ..io.print("正在上传",sendSize,contentLength);\n }\n); !web_rest_api.sendFile("上传文件路径",进度回调函数,MIME,分块大小) = @.sendFile( "上传文件路径" \n ,function(sendData,sendSize,contentLength,remainSize){\n __/*上传进度回调函数以及其他参数都可以省略,\n如果打开文件失败,sendFile 函数返回 null 与错误信息,\n否则返回响应数据*/\n }\n); !web_rest_api.receiveFile( = 配置下次下载文件的参数,\n此配置在后续用 get,post 等函数提交请求后自动清空\n\n此函数成功返回对象自身,创建文件失败返回 null,错误信息 !web_rest_api.receiveFile(.(savePath,callback,saveDir) = 配置下次下载文件的参数,\n此配置在后续用 get,post 等函数提交请求后自动清空\n此函数返回对象自身\n\n@savePath 参数指定下载文件路径,\n可选用 @saveDir 指定下载根目录,指定此参数则参数 @1 应为相对路径。\n可选用 @callback 参数指定下载进度回调函数,\n进度回调函数参数依次为 recvData,recvSize,contentLength,\n其中 recvData 为本次下载的数据,\nrecvSize 为本次下载的字节数,\ncontentLength 为需要下载的总字节数 !web_rest_api.receiveFile() = !web_rest_api. !web_rest_api.getUrl() = 返回当前对象发送请求使用的 URL 地址,\n如果有远程函数与此函数同名,写为 ["/getUrl"] 即可 !web_rest_api.getGet(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 get["/get"] 的缩略写法 !web_rest_api.postPost(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 post["/post"] 的缩略写法 !web_rest_api.headHead(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 head["/head"] 的缩略写法 !web_rest_api.putPut(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 put["/put"] 的缩略写法 !web_rest_api.deleteDelete(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 delete["/delete"] 的缩略写法 !web_rest_api.patchPatch(__) = 返回 HTTP API 调用对象,\nURL 资源名称与 HTTP 请求方法都设为 "get"\n这是 patch["/patch"] 的缩略写法 !web_rest_api.getGet() = !web_rest_api. !web_rest_api.postPost() = !web_rest_api. !web_rest_api.headHead() = !web_rest_api. !web_rest_api.putPut() = !web_rest_api. !web_rest_api.deleteDelete() = !web_rest_api. !web_rest_api.patchPatch() = !web_rest_api. end intellisense**/ /**details(说明) 如果 URL 模板中的变量出现在 URL 路径中, 那么实际调用接口时被应用到 URL 路径中的模板实参内的中文字符 默认会自动以 UTF-8 编码并 URLEncode 编码发送,这是标准方式与具体的页面编码无关。 如果需要在 URL 内使用非 UTF-8 编码(不太可能有这种情况),可事先对模板实参进行 URL 编码以去掉中文字符。 end details**/