task-2: buildJavaUrl() 统一 URL 构造(query 编码、尾斜杠规范化)
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
/** Java 主后端 URL 构造:常量表值已含 /api 前缀,此处只加 /newApi 网关前缀 */
|
||||||
|
export const JAVA_API_PREFIX = '/newApi/api'
|
||||||
|
|
||||||
|
function buildQueryString(query?: Record<string, unknown>): string {
|
||||||
|
if (!query) return ''
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
for (const [key, value] of Object.entries(query)) {
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
params.append(key, String(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const text = params.toString().replace(/\+/g, '%20')
|
||||||
|
return text ? `?${text}` : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildJavaUrl(
|
||||||
|
endpoint: string,
|
||||||
|
query?: Record<string, unknown>,
|
||||||
|
): string {
|
||||||
|
const normalized = endpoint.endsWith('/') ? endpoint.slice(0, -1) : endpoint
|
||||||
|
const prefix = normalized.startsWith('/api')
|
||||||
|
? '/newApi'
|
||||||
|
: JAVA_API_PREFIX
|
||||||
|
return `${prefix}${normalized}${buildQueryString(query)}`
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { test } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { buildJavaUrl } from '../src/shared/api/url.ts'
|
||||||
|
|
||||||
|
test('test_build_url_prefixed', () => {
|
||||||
|
assert.equal(buildJavaUrl('/api/dedupe/run'), '/newApi/api/dedupe/run')
|
||||||
|
assert.equal(buildJavaUrl('/api/similar-asin/parse'), '/newApi/api/similar-asin/parse')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_with_query', () => {
|
||||||
|
const url = buildJavaUrl('/api/dedupe/history', { resultId: 42 })
|
||||||
|
assert.equal(url, '/newApi/api/dedupe/history?resultId=42')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_no_query', () => {
|
||||||
|
const url = buildJavaUrl('/api/publish/dashboard')
|
||||||
|
assert.ok(!url.includes('?'), '无 query 时不应追加 ?')
|
||||||
|
assert.equal(url, '/newApi/api/publish/dashboard')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_query_encoding', () => {
|
||||||
|
const url = buildJavaUrl('/api/query-asin/tasks', {
|
||||||
|
shopName: 'a b&c',
|
||||||
|
keyword: '中文',
|
||||||
|
})
|
||||||
|
const encoded = encodeURIComponent('a b&c')
|
||||||
|
assert.equal(url, `/newApi/api/query-asin/tasks?shopName=${encoded}&keyword=${encodeURIComponent('中文')}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_empty_query_object', () => {
|
||||||
|
const url = buildJavaUrl('/api/withdraw/candidates', {})
|
||||||
|
assert.ok(!url.includes('?'), '空对象不应追加 ?')
|
||||||
|
assert.equal(url, '/newApi/api/withdraw/candidates')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_trailing_slash_endpoint', () => {
|
||||||
|
const url = buildJavaUrl('/api/split/run/')
|
||||||
|
assert.equal(url, '/newApi/api/split/run', 'endpoint 尾斜杠应被规范化')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_undefined_query', () => {
|
||||||
|
const url = buildJavaUrl('/api/files/upload', undefined)
|
||||||
|
assert.ok(!url.includes('?'), 'undefined query 不应追加 ?')
|
||||||
|
assert.equal(url, '/newApi/api/files/upload')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_build_url_join_no_duplicate_slash', () => {
|
||||||
|
assert.equal(buildJavaUrl('/api/dedupe/run'), '/newApi/api/dedupe/run')
|
||||||
|
assert.ok(!buildJavaUrl('/api/dedupe/run').includes('/newApi//api'))
|
||||||
|
assert.ok(!buildJavaUrl('/api/dedupe/run').includes('//api/'))
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user