整合測試 - 使用 Flurl 簡化建立 QueryString

其實這也不一定是在整合測試才會用到,一般會呼叫使用到外部 API 時的情境也一樣適用。

當要將一個物件的資料帶入 QueryString 時,每次就會覺得這個步驟很麻煩,甚至有一段時間是自己寫個方法來處理,但總覺得不是一個漂亮的解決方式,

於是就想到可以使用 Flurl 來處理,也的確是可以簡化以前那些繁瑣的建立 QueryString 的作法。

例如我有一個 web api  是 /api/member,為 Get 方法,並且需要帶入以下的資料才能夠取得資料

api/member?serviceId={serviceId}&webSiteId={webSiteId}&memberNo={memberNo}&memberType={memberType}

public class MemberGetParameter
{
    /// <summary>
    /// ServiceId (必要)
    /// </summary>
    [Required]
    public string ServiceId { get; set; }

    /// <summary>
    /// WebSiteId (必要)
    /// </summary>
    [Required]
    public string WebSiteId { get; set; }

    /// <summary>
    /// MemberNo (必要)
    /// </summary>
    [Required]
    public string MemberNo { get; set; }

    /// <summary>
    /// 會員類別 (必要)
    /// </summary>
    [Required]
    public MemberType MemberType { get; set; }
}

當然屬性數量少的時後是有人會默默地選擇使用自己一個一個將 Key Value  手動帶入

但是身為一個「懶惰」的程式開發人員,無法接受這樣的狀況,而且往後還會有需要帶入更多值到 QueryString 的情境,所以我就建立了這麼一個方法來處理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;

namespace Sample.Web.IntegrationTestResource.Helpers;

/// <summary>
/// class UrlUtilities.
/// </summary>
public static class UrlUtilities
{
    public static QueryString ConvertToQueryString(object obj, bool enableUrlEncode = false)
    {
        var keyValues = from p in obj.GetType().GetProperties()
                        where p.GetValue(obj, null) is not null
                        select new KeyValuePair<string, string>
                        (
                            key: $"{p.Name}",
                            value: enableUrlEncode
                                ? $"{HttpUtility.UrlEncode($"{p.GetValue(obj, null)}")}"
                                : p.PropertyType == typeof(DateTime)
                                    ? $"{p.GetValue(obj, null):yyyy-MM-ddTHH:mm:ss.fff}"
                                    : $"{p.GetValue(obj, null)}"
                        );

        var queryBuilder = new QueryBuilder(keyValues);
        var queryString = queryBuilder.ToQueryString();
        return queryString;
    }
}

實際的使用情況如下

	// arrange
	var parameter = new MemberGetParameter
	{
		ServiceId = "wertyuioldsfghjkk",
		WebSiteId = "456yuhgjbn9u8j",
		MemberType = MemberType.Normal,
		MemberNo = "12345678"
	};

	var requestUrl = string.Concat("/api/Member", UrlUtilities.ConvertToQueryString(parameter));

	// act
	var response = await client.GetAsync(requestUrl);

也使用了好一段時間,也沒有發生什麼太大的問題,但總覺得應該可已有更好的解決方式可以替代。

有關如何建立 QueryString 的方式有很多種,比較標準的作法都會使用到 QueryBuilder 類別,不妨可以參考以下的連結內容:

使用 Flurl

我所使用到的是 Fluent URL Building  的部分

Fluent URL Building - Flurl

看到提供的範例,應該就知道可以達到什麼樣的效果

var url = "http://www.mysite.com".SetQueryParam("x", new[] { 1, 2, 3 });
Assert.AreEqual("http://www.mysite.com?x=1&x=2&x=3", url)

所以我就把原本的程式通通改用 Flurl 來處理 Get 情境下的 QueryString 了

	// arrange
	var parameter = new MemberGetParameter
	{
		ServiceId = "wertyuioldsfghjkk",
		WebSiteId = "456yuhgjbn9u8j",
		MemberType = MemberType.Normal,
		MemberNo = "12345678"
	};

	var requestUri = "/api/Member".SetQueryParams(parameter).ToUri();

	// act
	var response = await client.GetAsync(requestUri);

有關 Flurl  的使用,可以參考 Flurl Documentation,或是以下的兩篇文章內容

以上

分享