我想为遵循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, url, true);
request.send(data);
});
}
//#endregion
//#region Public
return function (url, method, data) {
url = window.apiBasePath + url;
return ajax(url, method, 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。客户端和url构建都将在mytools命名空间中创建。此外,变量headerToken和apiBasePath将在rest调用后在mytools构造函数中分配。
在这种情况下使用什么设计模式?请记住,我想要一个非ES6解决方案。
实际上只有两种方法可以做你想做的事情;每个路由(dashboard
、basket
等(都是具有fetch
行为等的完整api对象,或者dashboard
等中的get()
方法映射回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和-es6类-a37b6c90c7f8
编辑动态URL
如果我理解正确的话,您将从REST调用中获得URL。
假设这个调用的返回类似于:
{
"dashboard": {
"url": "dashboard/",
"token": "..."
},
"basket": {
"url": "basket/",
"token": "..."
},
...
}
在这一点上,您现在可以浏览并创建您的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更易于使用(是预编译——就像在编写代码时一样。然而,直到运行时才知道您的路线