ArcGIS JSAPI in depth之网络请求封装基于Web的GIS应用中离不开网络请求,网络请求封装是一个看似简单却极其关键的环节。无论是处理复杂的 API 调用,还是优化性能与用户体验,一个高效、健壮的网络请求封装方案都能为你的项目带来质的飞跃。
在 ArcGIS Maps SDK for JavaScript 中,通过esriRequest这个全局封装的网络请求函数,为前端SDK应用与后端GIS服务引擎之间的网络请求提供了灵活可靠的企业级网络请求工具库。其中融入了大量高级技巧和最佳实践,展现了极高的工程化水平和设计思维。通过阅读底层的源码细节,我们能学习到如何在自己的软件产品或是框架设计中融合企业级的研发逻辑,优化网络请求。
让我们一起踏上这段源码探索之旅,解锁网络请求封装的高级技巧!
在SDK中通过 @arcgis/core/request 模块提供的全局函数esriRequest封装了一个异步请求的函数,用于发送HTTP请求,在此基础上,处理了请求的配置、拦截器、身份验证、代理、超时、错误处理等,共同构成了网络请求工具函数的核心功能。
函数签名
- request(url, requestOptions)
简要处理流程
request 函数的核心处理流程基于上述的 6 个步骤,分别是:
1. 初始化请求URL与请求配置
函数支持URL对象或是字符串的形式传递请求地址,在内部统一转换为字符串。同时,如果通过第二个参数传递类型为 URLSearchParams 的对象作为query条件,那么在内部同样会将其转换为查询字符串。
最后,对于非blob与data协议的请求,会对请求的url地址进行统一的规范化处理,并将初始化完成的请求url与请求参数合并为一个统一对象用于后续处理。
// ... ...
url instanceof URL && (url = e.toString());
opts?.query instanceof URLSearchParams && (opts.query = l(t.query.toString().replaceAll("+", " ")));
isBlob(url) || isData(url) || url = normalize(url)
const req = {
url: url,
requestOptions: { ...opts }
}
2. 拦截器处理
请求函数分两步针对内部和外部的拦截器进行分别的处理。如果存在内部拦截器,则执行拦截器的逻辑。如果拦截器返回了数据,则直接返回该数据。对于外部拦截器同理,如果拦截器没有返回数据,则继续后续流程。这里,提供了一个良好的范式,如果客户端应用通过配置的方式为特定的URL指定了拦截器返回值,则意味着该请求完全由客户端响应,并不会实际发出真实的网络请求,在开发和调试环节需要进行网络请求模拟时具有实际的使用场景。
// ... ...
const internalInterceptor = getInterceptor(url, esriConfig.internalInterceptors)
if (internalInterceptor) {
const respInterceptor = await executeInterceptor(internalInterceptor, req)
if (null != respInterceptor) return createAbortError(respInterceptor)
}
let interceptor = getInterceptor(url)
if (interceptor) {
const respInterceptor = await executeInterceptor(interceptor, req)
if (null != respInterceptor) return createAbortError(respInterceptor)
interceptor.after || interceptor.error || interceptor = null
}
3. 请求校验
由于SDK不仅仅支持在客户端浏览器环境下运行,同时还能支持node环境,同时,网络请求也可能由Web Worker在单独的线程中发起,这里在请求的校验环节对运行时环境和请求方法进行了检查,当不满足条件时则抛出异常终止请求。这里,值得学习的是对于请求的各种校验都发生在拦截器执行之后,这也更加便于使用者在多种环境下更好的利用拦截器进行请求模拟。
4. 初始化控制器
AbortController 作为新晋的W3C标准化网络控制器,允许客户端代码提前终止网络请求,主要用于多种需要控制超时和取消的场景中。SDK同样通过signal参数兼容了AbortController的行为。这一步主要对AbortController控制器进行初始化,并通过闭包的方式构造请求取消函数。
const controller = new AbortController()
const abortHandle = onAbort(requestOptions, () => controller.abort());
5. 配置请求参数
这一步主要是对内部使用的完整的请求参数对象进行重建,以便于后续的扩展性。可以看到这一步构建的请求参数中预留了大量的高阶能力配置,包括重试、代理、安全性相关等等。
const opts = {
controller: controller,
credential: void 0,
credentialToken: void 0,
fetchOptions: void 0,
hasToken: !1,
interceptor: interceptor,
params: req,
redoRequest: !1,
useIdentity: esriConfig.useIdentity,
useProxy: !1,
useSSL: !1,
withCredentials: !1
};
6. 发出请求
最后,通过内部的执行函数发出网络请求,并通过after拦截器对请求的响应体进行再加工。拦截器中的after函数对请求的响应进行同步处理,之后将响应对象返回给调用者。
// ... ...
const resp = await doRequest(opts).finally((() => abortHandle?.remove()));
interceptor?.after?.(resp)
return resp
以上是SDK中提供的网络请求封装的初步处理流程,后续我们将进一步了解关于请求执行的更多细节。
如果你觉得本文对你有些许启发,请持续关注我的公众号“戈伊星球”吧!