更新答案:
在你说过的评论中:
查询参数的顺序确实无关紧要,但路径变量对于不同的路径是不同的(因此不可能只获取 id 和代码 - 我假设必须使用正则表达式或字符串匹配)
是的,您可以从路径中提取 [] 中的位并使用它们,稍后请记住您不希望它们出现在查询字符串中:
function buildPath(path, params) {
const used = new Set();
// Replace the parts in [xxx]
path = path.replace(/\[([^\]]+)]/g, (m, c0) => {
used.add(c0);
return c0 in params ? params[c0] : "";
});
// Add query string if there are any left over
let qstr = Object.entries(params)
.filter(([key]) => !used.has(key))
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join("&");
return path + (qstr && "?" + qstr);
}
现场示例:
function buildPath(path, params) {
const used = new Set();
// Replace the parts in [xxx]
path = path.replace(/\[([^\]]+)]/g, (m, c0) => {
used.add(c0);
return c0 in params ? params[c0] : "";
});
// Add query string if there are any left over
let qstr = Object.entries(params)
.filter(([key]) => !used.has(key))
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join("&");
return path + (qstr && "?" + qstr);
}
console.log("Your example:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, first: 'xyz', second: 2, code: 'abc'}));
console.log("Example without any query string:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, code: 'abc'}));
console.log("Your example with `first` and `second` reversed:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, second: 2, first: 'xyz', code: 'abc'}));
console.log("An example requiring URI encoding:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, blah: "value with & that needs encoding", code: 'abc'}));
.as-console-wrapper {
max-height: 100% !important;
}
如果您需要支持数组值,请参见下面的第二个代码块;您可以对其进行调整以将其添加到上述内容中。
如果您需要支持没有 Set 且不想使用 polyfill 的过时环境,则可以使用对象来跟踪使用过的键:
// ES5 version
function buildPath(path, params) {
var used = Object.create(null); // So it doesn't inherit from Object.prototype and have "toString", etc.
// Replace the parts in [xxx]
path = path.replace(/\[([^\]]+)]/g, function(m, c0) {
used[c0] = true;
return c0 in params ? params[c0] : "";
});
// Add query string if there are any left over
let qstr = Object.keys(params)
.filter(function(key) { return !used[key]; })
.map(function(key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
})
.join("&");
return path + (qstr && "?" + qstr);
}
console.log("Your example:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, first: 'xyz', second: 2, code: 'abc'}));
console.log("Example without any query string:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, code: 'abc'}));
console.log("Your example with `first` and `second` reversed:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, second: 2, first: 'xyz', code: 'abc'}));
console.log("An example requiring URI encoding:");
console.log(buildPath("/test/[id]/[code]/path", {id: 1, blah: "value with & that needs encoding", code: 'abc'}));
.as-console-wrapper {
max-height: 100% !important;
}
原答案:
如果查询参数的顺序无关紧要(或者如果对象始终以相同的方式创建并且属性添加到它的顺序与您希望它们在查询参数中的顺序相匹配),您可以抓住 @ 987654328@ 和 code 从对象中捕获并捕获所有其余部分以构建查询字符串,如下所示:
function buildPath(path, params) {
const {id, code, ...query} = params;
return path.replace(/\[id]/, id)
.replace(/\[code]/, code) +
"?" +
Object.entries(query).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join("&");
}
// Your example
console.log(buildPath("/test/[id]/[code]/path", {id: 1, first: 'xyz', second: 2, code: 'abc'}));
// Your example with `first` and `second` reversed:
console.log(buildPath("/test/[id]/[code]/path", {id: 1, second: 2, first: 'xyz', code: 'abc'}));
// An example requiring URI encoding
console.log(buildPath("/test/[id]/[code]/path", {id: 1, blah: "value with & that needs encoding", code: 'abc'}));
不过,请再次注意,查询字符串参数的顺序(在现代浏览器上)将与在对象上创建属性的顺序相匹配(请注意上面前两个示例之间的区别)。不过,对于具有不同键的参数,这通常对服务器无关紧要。
这还假设您永远不会将数组作为要作为重复查询参数发送的值。例如,它发送:
{id: 1, code: 'abc', blah: ["one", "two", "three"]}
作为/test/1/abc/path?blah=one%2Ctwo%2Cthree。在某些环境中,您希望改为重复 blah 查询参数名称。如果是这样,您需要检测数组并处理它:
function buildPath(path, params) {
const {id, code, ...query} = params;
const qstr = Object.entries(query).map(([key, value]) => {
key = encodeURIComponent(key) + "[]";
if (Array.isArray(value)) {
value = value.map(e => encodeURIComponent(e)).join(`&${key}=`);
} else {
value = encodeURIComponent(value);
}
return `${key}=${value}`;
}).join("&");
return path.replace(/\[id]/, id)
.replace(/\[code]/, code) +
qstr;
}
// Your example
console.log(buildPath("/test/[id]/[code]/path", {id: 1, first: 'xyz', second: 2, code: 'abc'}));
// Your example with `first` and `second` reversed:
console.log(buildPath("/test/[id]/[code]/path", {id: 1, second: 2, first: 'xyz', code: 'abc'}));
// An example requiring URI encoding
console.log(buildPath("/test/[id]/[code]/path", {id: 1, blah: "value with & that needs encoding", code: 'abc'}));
// An example with an array converted to repeated query params
console.log(buildPath("/test/[id]/[code]/path", {id: 1, code: 'abc', blah: ["one", "two", "three"]}));
某些环境希望在以这种方式使用密钥后看到[]。如果是这样,只需更改
key = encodeURIComponent(key);
到
key = encodeURIComponent(key) + "[]";
在map 回调中。