如何包装自定义json:api客户端并使调用流畅和异步(Non-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, 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解决方案。

实际上只有两种方法可以做你想做的事情;每个路由(dashboardbasket等(都是具有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对象中的任何其他内容。不过,这确实意味着一些事情:

  1. 在进行初始REST调用之前,您无法访问mytools.api.dashboard等-它不会指向任何东西
  2. 您需要事先知道您的路线-例如,在这里,我们知道mytools.api.dashboard变量后面有一条路线

关于第2点,如果不重新思考如何处理问题,就不可能真正拥有真正的动态url(即,您可以将"profile"添加到REST JSON中,而无需在代码库中添加相应的mytools.api.profile调用(。您在这里所做的(使API更易于使用(是预编译——就像在编写代码时一样。然而,直到运行时才知道您的路线

最新更新