我有这个代码正在构建一个记录不同参数的url端点:
const query = (obj) => {
let starter = 'query='
let queryToString = JSON.stringify(obj)
return `${starter}${queryToString}`
}
const params = (str) => `${str}`
const endpoint = (protocol, base, params, query) => {
if (!params && !query) return `${base}`
if (!params) return `${base}${query}`
return `${protocol}${base}?${params}&${query}`
}
const baseUrl = 'api.content.io'
const protocol = (secure = true) => secure ? 'https://' : 'http://'
let result = endpoint(protocol(), baseUrl, params('limit=5&order=desc'),
query({
field: 'title',
include: 'brands'
}));
console.log(result)
这会构建一个字符串,如:
https: //api.content.io?limit=5&order=desc&query={"field":"title","include":"brands"}
是否可以重构此代码,以便可以删除endpoint
函数中的条件,应用正确的连接字符串并将整个事物链接到函数调用中,如< / p>
Endpoint.chain(protocol(p)).chain(base(b)).chain(params(p)).chain(query(q)).build()
我如何开始这样做?
更新:下面的解决方案非常好,但我想了解功能程序员如何使用Monads的ADT(代数数据类型)来解决这个问题。我们的想法是运行chain
个函数,然后运行fold
以获取我想要的值
答案 0 :(得分:2)
链接基本上发生在每个函数调用中返回完整对象时。这里每个函数都设置其给定块的值,并返回该对象以用于它所附加的任何上下文。每个函数都能够处理它接收的值,而value函数将根据设置的参数构建url。
var Endpoint = {
protocol: protocol,
base: base,
params: params,
query: query,
value: value
}
function protocol(b) {
protocol.value = b && 'https://' || 'http://';
return this;
}
function base(s) {
base.value = s || '';
return this;
}
function params(s) {
params.value = s || '';
return this;
}
function query(s) {
query.value = s || '';
return this;
}
function value(s) {
return '$protocol$base$params$params$query'
.replace('$protocol',protocol.value)
.replace('$base',base.value)
.replace('$params',params.value)
.replace('$query',query.value)
}
Endpoint
.protocol(true)
.base('www.foo.com')
.params('one/two/three')
.query('?foo=bar')
.value()
// "https://www.foo.comone/two/three$params?foo=bar"
答案 1 :(得分:2)
你可以使用一个类,它有允许链接的方法(它们返回this
):
class EndPoint {
constructor() {
// defaults
this.prot = 'https://';
this.bas = 'api.content.io';
this.qry = '';
this.par = '';
}
secure(on = true) {
this.prot = on ? 'https://' : 'http://';
return this;
}
base(str) {
this.bas = str;
return this;
}
query(obj) {
this.qry = `query=${JSON.stringify(obj)}`
return this;
}
params(str) {
this.par = str
return this;
}
build() {
let sep1 = this.qry.length || this.par.length ? '?' : '';
let sep2 = this.qry.length && this.par.length ? '&' : '';
return `${this.prot}${this.bas}${sep1}${this.par}${sep2}${this.qry}`;
}
};
let result = new EndPoint().secure(true).base('www.example.com')
.params('limit=5&order=desc').query({ field: 'title', include: 'brands' }).build();
console.log(result);
&#13;
答案 2 :(得分:1)
虽然上述答案可行,但如果您希望语法以Endpoint().
开头,则使用EndpointBuilder类和下面的工厂函数。
class EndpointBuilder {
constructor() {
this.params = [];
this.protocol = 'http://';
this.baseUrl = 'api.endpoint.io';
}
base(url) {
if (url && url.trim().length > 0)
this.baseUrl = url;
return this;
}
secure() {
this.protocol = 'https://';
return this;
}
setParam(param, val) {
if (param && val)
this.params.push({ param, val });
return this
}
toString() {
const url = `${this.protocol}${this.baseUrl}`;
if (this.params.length <= 0)
return url;
let qString = '';
this.params.forEach(p => {
qString = `${qString}${qString.length > 0 ? '&' : ''}${p.param}=${JSON.stringify(p.val)}`;
});
return `${url}?${qString}`;
}
};
// Endpoint Factory
const Endpoint = function () {
return new EndpointBuilder();
};
用法示例:
const url1 = Endpoint().setParam('limit', 5).setParam('query', { field: 'title', include: 'brands' }).toString();
const url2 = Endpoint().base('another.endpoint.io').setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString();
const url3 = Endpoint().base('another.endpoint.io').secure().setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString();
console.log(url1);
// http://api.endpoint.io?limit=5&query={"field":"title","include":"brands"}
console.log(url2);
// http://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"}
console.log(url3);
// https://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"}