如何解决如何包装自定义 json:api 客户端并使调用流畅和异步非 ES6
我想为遵循 json:api 规范的 Restful API 创建自定义客户端。所以,我创建了这个支持异步的简单客户端:
MyTools.Api = (function () {
"use strict";
//#region Private
function ajax(url,method,data) {
return new Promise(function (resolve,reject) {
let request = new XMLHttpRequest();
request.responseType = 'json';
//Open first,before setting the request headers.
request.open(method,url,true);
request.setRequestHeader('Authorization','Bearer ' + window.headerToken)
request.onreadystatechange = function () {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status === 200) {
resolve(request.response);
} else {
reject(Error(request.status));
}
}
};
request.onerror = function () {
reject(Error("Network Error"));
};
request.open(method,true);
request.send(data);
});
}
//#endregion
//#region Public
return function (url,data) {
url = window.apiBasePath + url;
return ajax(url,data);
};
//#endregion
})(MyTools.Api || {});
我将令牌从后端 (.net) 传递到名为 headerToken 的窗口全局变量。与基本路径(apiBasePath)相同。 现在,我可以像这样调用这个客户端
CTools.Api("/dashboard/users/","GET").then(function (result) {
console.log(result);
});
我的目标是创建一种更流畅的方式来使用 api。例如,我想调用类似的东西:
mytools.api.dashboard.users.get().then(function (result) {
console.log(result);
});
和
mytools.api.dashboard.users.get(fliteroptions).then(function (result) {
console.log(result);
});
如果有另一个模块可以使用
mytools.api.basket.items.get(fliteroptions).then(function (result) {
onsole.log(result);
});
仪表板和购物篮将具有不同的网址。客户端和 url 构建都将在 mytools 命名空间内创建。此外,变量 headerToken 和 apiBasePath 将在休息调用后在 mytools 构造函数中分配。
在这种情况下使用什么设计模式? 请记住,我想要一个非 ES6 的解决方案。
解决方法
基本上只有两种方法可以做你想做的事情;每个路由(dashboard
、basket
等)都是具有 fetch
行为等的完整 api 对象,或者 get()
等中的 dashboard
方法映射回api
。
像这样:
class Route {
constructor(url,api) {
this.url = url;
this.api = api;
}
get(filterOptions) {
return this.api.get(this.url,filterOptions)
}
post(filterOptions) {
return this.api.post(this.url,filterOptions);
}
}
class Api {
constructor(basePath) {
this.basePath = basePath;
}
get(route,filterOptions) {
// filterOptions can be null here
return fetch(`${this.basePath}/${route}`)
.then(data => data.json())
}
post(route,filterOptions) {
// filterOptions can be null here
...
}
...
}
mytools.api = new Api();
mytools.api.dashboard = new Route('dashboard',mytools.api);
mytools.api.basket = new Route('basket',mytools.api);
...
您的 Route
类是一个简单的类,其中包含 url
和其他任何内容,以及实际执行提取操作的 api
。
Api
包装您的 fetch
并处理任何传递的 filterOptions
。
创建 Api
时,您也创建了所有路由。
缺点是您需要事先了解所有路线。
要更动态地执行此操作,您可以查看Proxy
:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
在这里,您可以实现一个每次访问您的对象时都会调用的函数:
var handler = {
get: function(target,name) {
return "Hello," + name;
}
};
var proxy = new Proxy({},handler);
console.log(proxy.world); // output: Hello,world
您应该能够挂钩它,以便 get
方法返回 Api
对象,然后您可以直接调用它
针对 ES5 进行编辑
我已经有一段时间没有编写纯 ES5 了,但是例如转换 Route
类应该看起来像这样:
function Route(url,api) {
this.url = url;
this.api = api;
}
Route.prototype.get = function(filterOptions) {
return this.api.get(this.url,filterOptions)
}
Route.prototype.post = function(filterOptions) {
return this.api.post(this.url,filterOptions)
}
创建对象应该保持不变 - new Route(url,api)
我不确定 fetch
是否是 ES5 - 您需要查看代码将在哪里运行以查看它是否受支持。与承诺同上;您要么需要使用 Bluebird
之类的库,要么使用回调。
有关课程的更多信息:https://medium.com/@apalshah/javascript-class-difference-between-es5-and-es6-classes-a37b6c90c7f8
修改动态网址
如果我没理解错的话,您是从 REST 调用中获取 URL。
假设这个调用的返回类似于:
{
"dashboard": {
"url": "dashboard/","token": "..."
},"basket": {
"url": "basket/",...
}
此时,您现在可以通过并创建您的 api:
mytools.api = new Api();
mytools.api.dashboard = new Route(jsonObj.dashboard.url,mytools.api);
mytools.api.basket = new Route(jsonObj.basket.url,mytools.api);
...
您还可以传入 token
或 REST JSON 对象中的任何其他内容。不过,这确实意味着一些事情:
- 在进行初始 REST 调用之前,您无法访问
mytools.api.dashboard
等 - 它不会指向任何内容 - 您需要事先了解您的路线 - 例如在这里,我们知道我们在
mytools.api.dashboard
变量后面有一个路由
关于第 2 点,您不能真正拥有真正动态的 url(即您可以将 "profile"
添加到 REST JSON 而无需在您的代码库中添加相应的 mytools.api.profile
调用)而无需重新思考你如何处理事情。您在这里所做的(使 API 更易于使用)是预编译 - 就像在编写代码时一样。但是,直到运行时才知道您的路线
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。