前正子專案因為設定檔有人網址會多 /
導致程式一些狀況,公司有人有特別寫組語法方法,這邊我思考正常內建方法可不可以解決這個問題,怎麼沒有人寫好 Library 分享給別人使用?探討有什麼更好方式去解這個問題。
心智圖
mindmap
(程式組 Url 方法)
操作 Url 方法
.Net
UriBuilder 物件
Java
Uri 物件
JavaScript
URL 物件
設定 QueryString 方法
.Net
NameValueCollection
Java
MultiValuedMap 非原生
JavaScript
URLSearchParams
.Net 實作方法
使用 UriBuilder
UriBuilder
是一個用於建立和操作 URI 的類別,適用於需要動態生成或修改 URI 的場景。它的用途包括構建 API 請求、處理用戶輸入的 URL、以及在應用程序中動態生成鏈接。相較於單純使用字串來紀錄 URI,它有以下的優點:
- 安全性:
UriBuilder
會自動處理 URI 中的特殊字元和編碼問題,避免了手動操作可能產生的錯誤。
1
2
3
4
5
6
7
8
9
|
UriBuilder uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "/api/values",
Query = "name=" + Uri.EscapeDataString("John Doe")
};
Uri uri = uriBuilder.Uri;
|
- 易於操作:
UriBuilder
提供了許多方法和屬性來操作 URI 的各個部分(例如 scheme、host、path、query 等),比起手動解析和組裝字串來得方便。
1
2
3
4
5
6
7
8
9
10
11
|
UriBuilder uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "/api/values",
Query = "name=" + Uri.EscapeDataString("John Doe")
};
uriBuilder.Dump();
Uri uri = uriBuilder.Uri;
uri.ToString().Dump();
|
參考資料
可以看到 Query 的 ?name=John%20Doe
,在 ToString()
會自動進行 URL 解碼,通常用於使用者輸入的 encodeUrl,不需要自己處理。

可以看到 Query 的?name=John%20Doe
,在 ToString()
會自動做 urldecode 動作,通常用於使用者輸入encodeUrl
可以做這個動作,不需要自己做這個處理。
- 可讀性:使用
UriBuilder
可以讓程式碼更具可讀性,因為它明確地表達了你正在操作 URI,而不僅僅是操作字串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
UriBuilder uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "/api/values"
};
Uri uri1 = uriBuilder.Uri;
uriBuilder.Query = "id=123";
Uri uri2 = uriBuilder.Uri;
uriBuilder.Query = "id=456";
Uri uri3 = uriBuilder.Uri;
|
- 可重用性:
UriBuilder
可以重複使用來建立具有相同基礎的多個 URI,這在需要建立許多相似 URI 的情況下非常有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
UriBuilder uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "/api/values"
};
Uri uri1 = uriBuilder.Uri;
uriBuilder.Query = "id=123";
Uri uri2 = uriBuilder.Uri;
uriBuilder.Query = "id=456";
Uri uri3 = uriBuilder.Uri;
|
Info
UriBuilder 和 Uri 使用方是哪裡不一樣?
UriBuilder 和 Uri 兩者都是用於處理 URI 的,但它們的使用方式和目的有所不同。
Uri 是一個不可變的類別,代表一個 Uniform Resource Identifier (URI)。一旦 Uri 物件被建立,其內容就不能更改。Uri 物件通常用於存儲和傳遞 URI。
1
|
Uri uri = new Uri("https://example.com/api/values?id=123");
|
UriBuilder
則是一個可變的類別,用於建立和修改 URI。你可以使用 UriBuilder
的各種屬性(例如 Scheme、Host、Path、Query 等)來設定 URI 的各個部分,然後使用 Uri 屬性或 ToString
方法來獲取最終的 URI。
1
2
3
4
5
6
7
8
9
|
UriBuilder uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "example.com",
Path = "/api/values",
Query = "id=123"
};
Uri uri = uriBuilder.Uri; // 從 UriBuilder 獲取 Uri
|
總的來說,如果你需要建立或修改 URI
,則應使用 UriBuilder
。如果你只需要存儲或傳遞 URI
,則應使用 Uri
。
Info
那我通常要改 Uri
資料要怎麼做?
由於 Uri
是不可變的,所以你不能直接修改 Uri
物件的內容。如果你需要修改 URI,你應該使用 UriBuilder
。
以下是一個範例,展示如何使用 UriBuilder
修改 URI:
1
2
3
4
5
6
7
8
9
10
11
|
Uri originalUri = new Uri("https://example.com/api/values?id=123");
// 使用原始的 Uri 建立 UriBuilder
UriBuilder uriBuilder = new UriBuilder(originalUri);
// 修改 UriBuilder 的內容
uriBuilder.Path = "/api/items";
uriBuilder.Query = "id=456";
// 從 UriBuilder 獲取新的 Uri
Uri newUri = uriBuilder.Uri;
|
在這個範例中,我們首先使用原始的 Uri
建立了一個 UriBuilder
。然後,我們修改了 UriBuilder
的 Path 和 Query。最後,我們從 UriBuilder
獲取了新的 Uri
。新的 Uri
是 “https://example.com/api/items?id=456"。
解決 URL 多餘的 / 問題 "
可以用 Path.Combine
解決這個問題。
組 URL 和 Query 使用 UriBuilder
和 NameValueCollection
來解決這個問題。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
UriBuilder uriBuilder = new UriBuilder("http", "example.com");
NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
query["param1"] = "value1";
query["param2"] = "value2";
string path1 = "/path";
string path2 = "/to/resource";
uriBuilder.Path = Path.Combine(path1, path2);
uriBuilder.Query = query.ToString();
Console.WriteLine(uriBuilder.ToString());
// path2 前面根目錄會把 path1 路徑去掉,所以串接時候不要把 path2 前面加 /
// http://localhost/to/resource
|
LinqPad 範例
Info
HttpUtility.ParseQueryString(string.Empty);
和 NameValueCollection query = new NameValueCollection();
差異?
NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
和 NameValueCollection query = new NameValueCollection();
兩者的主要差別在於他們的初始化方式。
HttpUtility.ParseQueryString(string.Empty)
會返回一個 NameValueCollection,並且這個集合的 ToString 方法被覆蓋以返回一個 URL 編碼的查詢字符串。這對於創建查詢字符串非常有用。
詳細可以參考 HttpQSCollection
這個類別。
1
2
3
4
|
NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
query["param1"] = "value1";
query["param2"] = "value2";
Console.WriteLine(query.ToString()); // 輸出 "param1=value1¶m2=value2"
|
另一方面,創建一個新的 NameValueCollection,但是它的 ToString 方法並未被覆蓋,所以它只會返回類型名稱。
1
2
3
4
|
NameValueCollection query = new NameValueCollection();
query["param1"] = "value1";
query["param2"] = "value2";
Console.WriteLine(query.ToString()); // 輸出 "System.Collections.Specialized.NameValueCollection"
|
因此,如果你需要創建一個查詢字符串,使用 HttpUtility.ParseQueryString(string.Empty)
會更方便。
{{</admonition>}}
Javascript
1
2
3
4
5
|
const url = 'http://localhost//example/author/admin///';
const parts = url.split('/');
const cleanedParts = parts.filter((part, index) => index === 0 || part !== '');
const cleanUrl = cleanedParts.join('/');
console.log(cleanUrl); // 輸出: http://localhost/example/author/admin/
|
new URL()
1
2
3
|
const url = new URL('https://example.com');
url.pathname = '/newpath';
console.log(url.href); // 輸出: https://example.com/newpath
|
QueryString
1
2
3
4
|
const myUrl = new URL('https://example.com');
myUrl.pathname = '/mypage';
myUrl.searchParams.append('city', 'Rome');
console.log(myUrl.href); // 輸出: https://example.com/mypage?city=Rome
|
{{<admonition type=“info”>}}
在 Node.js 中,你可以使用 path
模組的 join
方法來組合路徑,並自動處理多餘的 /
。以下是一個範例:
1
2
3
4
|
const path = require('path');
let combinedPath = path.join('/foo', 'bar', '/baz/asdf', 'quux', '..');
console.log(combinedPath); // 輸出: '/foo/bar/baz/asdf'
|
在這個範例中,path.join
方法將多個路徑片段組合成一個路徑,並自動處理多餘的 /
和 ..
。
請注意,這個方法是 Node.js 特有的,並不適用於在瀏覽器中運行的 JavaScript。在瀏覽器中,你可能需要自己寫函數來處理路徑的組合和正規化。
Info
在瀏覽器中運行的 JavaScript 沒有內建的路徑組合函數,但你可以自己寫一個簡單的函數來實現這個功能。以下是一個範例:
1
2
3
4
5
6
7
8
9
|
function combinePaths(...paths) {
return paths
.join('/')
.replace(/\/+/g, '/') // 將多個連續的 '/' 替換為單個 '/'
.replace(/\/+$/, ''); // 移除結尾的 '/'
}
let combinedPath = combinePaths('/foo/', '/bar', '/baz/asdf/', '/quux', '..');
console.log(combinedPath); // 輸出: '/foo/bar/baz/asdf/quux/..'
|
這個 combinePaths
函數接受任意數量的路徑片段,將它們組合成一個路徑,並自動處理多餘的 /
。
Java
相對 .Net 的 Path.Combine
方法,Java 有 Paths.get
方法可以使用。
1
2
3
4
5
6
7
8
9
10
|
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
class HelloWorld {
public static void main(String[] args) {
String path = Paths.get("/", "//path", "/to", "///resource").toString();
System.out.println(path.toString());
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import org.apache.http.client.utils.URIBuilder;
public class Main {
public static void main(String[] args) throws Exception {
URIBuilder uriBuilder = new URIBuilder();
uriBuilder.setScheme("http")
.setHost("example.com")
.setPath("/path/to/resource")
.setParameter("param1", "value1")
.setParameter("param2", "value2");
System.out.println(uriBuilder.build());
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("param1", "value1"));
params.add(new BasicNameValuePair("param2", "value2"));
String queryString = URLEncodedUtils.format(params, Charset.forName("UTF-8"));
System.out.println(queryString);
}
}
|
Java 類似 .Net 的 NameValueCollection 物件
在 Java 中,對應 .Net 的 HTTP QueryString 可以用 NameValueCollection 去組合字串,但 Java 沒有原生方法。
在 Java 中,您可以使用 Multimap 來存儲具有重複鍵的值。以下是一些選項:
Apache Commons Collections:
使用 Multimap,它支持重複的鍵和值對。
您可以使用 org.apache.commons.collections4.MultiMap 來實現。
Java 8+:
使用 Map<K, List<V>>
,其中每個鍵都對應到一個值列表。
您可以使用 computeIfAbsent 方法來簡化操作。
總之,如果您需要一個類似於 NameValueCollection 的物件,您可以使用 Java 中的 Multimap 或 Map<K, List<V>>
。🌟