如何包装自定义 json:api 客户端并使调用流畅且异步(非 ES6)
How to wrap a custom json:api client and make calls fluent and async (Non-ES6)
我想为 restful API 创建一个遵循 json: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 的 window 全局变量。与基本路径 (apiBasePath) 相同。
现在,我可以像这样称呼这个客户
CTools.Api("/dashboard/users/", "GET").then(function (result) {
console.log(result);
});
我的目标是创建一种更流畅的方式来使用 api.For 示例,我想调用如下内容:
mytools.api.dashboard.users.get().then(function (result) {
console.log(result);
});
和
mytools.api.dashboard.users.get(fliteroptions).then(function (result) {
console.log(result);
});
并且如果有另一个模块可以使用 like
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
class 是一个简单的 class ,它包含一个 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
方法 return 成为 Api
对象,然后您可以直接调用它
为 ES5 编辑
自从我编写纯 ES5 以来已经有一段时间了,但是转换 Route
class,例如,应该看起来像这样:
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
这样的库,要么使用回调。
有关 classes 的更多信息,请点击此处:https://medium.com/@apalshah/javascript-class-difference-between-es5-and-es6-classes-a37b6c90c7f8
编辑动态 URL
如果我没听错的话,您是从 REST 调用中获取 URL。
假设此调用的 return 类似于:
{
"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 更易于使用)是预编译的 - 就像您在编写代码时一样。但是,直到运行时才知道您的路线
我想为 restful API 创建一个遵循 json: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 的 window 全局变量。与基本路径 (apiBasePath) 相同。 现在,我可以像这样称呼这个客户
CTools.Api("/dashboard/users/", "GET").then(function (result) {
console.log(result);
});
我的目标是创建一种更流畅的方式来使用 api.For 示例,我想调用如下内容:
mytools.api.dashboard.users.get().then(function (result) {
console.log(result);
});
和
mytools.api.dashboard.users.get(fliteroptions).then(function (result) {
console.log(result);
});
并且如果有另一个模块可以使用 like
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
class 是一个简单的 class ,它包含一个 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
方法 return 成为 Api
对象,然后您可以直接调用它
为 ES5 编辑
自从我编写纯 ES5 以来已经有一段时间了,但是转换 Route
class,例如,应该看起来像这样:
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
这样的库,要么使用回调。
有关 classes 的更多信息,请点击此处:https://medium.com/@apalshah/javascript-class-difference-between-es5-and-es6-classes-a37b6c90c7f8
编辑动态 URL
如果我没听错的话,您是从 REST 调用中获取 URL。
假设此调用的 return 类似于:
{
"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 更易于使用)是预编译的 - 就像您在编写代码时一样。但是,直到运行时才知道您的路线