CODE–URL調整HTTP/HTTPS、Port、QueryString參數公用函式

前陣子談過用 HttpUtility.ParseQueryString 解析、修改及還原參數的簡便做法,一不做二不休,再來聊聊如果拿到一個 URL, HTTP 要改 HTTPS、主機名稱要換、Port、路徑要改,是不是也有不走字串比對置換的優雅做法?

爬文查到好用的 .NET 內建物件 - UriBuilder(.NET 2.0 時代就有了,我到現在才學會),Scheme(http、https)、Port、Host、Path 均可任意抽換調整,再產生新的 URL,比起自己寫 Regex 比對置省事可靠不少。UriBuilder 已滿足大部分所需,只少了將 QueryString 轉成 NameValueColletion 方便抽換修改參數,查了文件 UriBuilder 並未宣告 seal 禁止繼承,因此繼承 UriBuilder 再擴充參數處理功能是不錯的選擇。

我寫了一顆 FlexUrlEditor 繼承 UriBuilder,建構式傳入既有 URL,融和上回將 QueryString 轉為 NameValueCollection 增刪修改再轉回字串的技巧,參數可透過 FlexUrlEditor["參數名"] 存取,為 UriBuilder 加上編修 QueryString 參數功能。此外,UriBuilder 產出的 URL 字串有個小缺點 - 它包含 :80、:443 等習慣上會省略的 Port,所以我在輸出時加了點工,使結果更符合平日慣用格式。程式碼如下:

using System;
using System.Collections.Specialized;
using System.Text;
using System.Web;

public class FlexUrlEditor : UriBuilder
{
    private readonly NameValueCollection collection;

    public FlexUrlEditor(string url) : base(url)
    {
        collection = 
            HttpUtility.ParseQueryString(new Uri(url).Query, Encoding.UTF8);
    }

    public string this[string key]
    {
        get { return collection[key]; }
        set
        {
            if (value == null && collection[key] != null)
            {
                collection.Remove(key);
            }
            else
            {
                collection[key] = value;
            }
        }
    }

    public string GenUrl()
    {
        //REF: https://goo.gl/gHmGUz
        base.Query = Uri.EscapeUriString(
            HttpUtility.UrlDecode(collection.ToString()));
        //remove 80/443 port for http/https
        if (base.Uri.IsDefaultPort) base.Port = -1;
        return base.ToString();
    }
}

寫幾行程式實測,試了修改、刪除與增加 QueryString 參數,https 改 http,Port 變更等(Host 與 Path 也都可置換忘了示範),均得到預期的結果。

using System;

namespace ConsoleApp1
{
    class Program
    {

        static void Main(string[] args)
        {
            //Add parameter
            var url = "http://blog2.darkthread.net/";
            var builder = new FlexUrlEditor(url);
            builder["a"] = "1";
            Console.WriteLine(builder.GenUrl());

            //modify and remove parameter
            url = "http://blog2.darkthread.net/?a=1&b=2&c=3";
            builder = new FlexUrlEditor(url);
            builder["b"] = null; //set null to remove
            builder["c"] = "中文"; //will be encoded
            Console.WriteLine(builder.GenUrl());

            //single command (C# 6.0+)
            var newUrl = new FlexUrlEditor(
                "https://blog2.darkthread.net/?a=1&b=2&c=3")
            {
                ["b"] = null,
                ["c"] = "中文"
            };
            Console.WriteLine(newUrl.GenUrl());

            //read encoded query parameter
            builder = new FlexUrlEditor(
                "http://blog2.darkthread.net/?a=1&b=%E4%B8%AD%E6%96%87");
            Console.WriteLine($"a={builder["a"]}, b={builder["b"]}");

            //change scheme & port
            builder = new FlexUrlEditor("https://blog2.darkthread.net");
            builder.Scheme = "http";
            builder.Port = 8888;
            Console.WriteLine(builder.GenUrl());

            Console.ReadLine();
        }
    }
}

執行結果如下:

工具箱再添順手工具一支~

歡迎推文分享:
Published 10 April 2018 09:52 PM 由 Jeffrey
Filed under:
Views: 4,203



意見

# Ammon said on 11 April, 2018 12:01 AM

用 IsDefaultPort 判斷是否為預設Port,將Port設定為-1,ToString時不會額外加上Port

if (Uri.IsDefaultPort) {

Port = -1;

}

# Jeffrey said on 11 April, 2018 04:57 AM

to Ammon, 這招漂亮! 感謝分享,已納入程式碼。

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<April 2018>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication