C#調用阿里雲CDN API刷新緩存


使用CDN必須要解決CDN緩存的問題,要么在每次更新文件時生成不同的URL,要么在每次更新文件時刷新CDN緩存。我們在一個實際應用場景中用到了后者,所以需要調用阿里雲CDN的API進行緩存刷新的操作。

刷新緩存本身的接口很簡單,只需要給Action與ObjectPath這2個參數傳值,比如:Action=RefreshObjectCaches&ObjectPath=test.com/test.jpg 。但是實際除了這2參數之外,還需要傳遞8個公共請求參數:Format, Version, Signature,SignatureMethod, SignatureNonce, SignatureVersion, AccessKeyId, Timestamp,其中的Signature(簽名結果串)的值計算很復雜,而阿里雲官網幫助文檔中只有python的示例代碼,而我們用的是C#,於是只能參考幫助文檔與python示例自己動手(用的是C# 6.0)。

針對這8個公共請求參數,定義了一個CdnRequest類,這個類有8個屬性對應這8個公共請求參數,並且根據文檔中的要求進行賦值。

public class CdnRequest
{
    public CdnResponseFormat Format { get; set; } = CdnResponseFormat.Json;

    public string Version { get; } = "2014-11-11";

    public string AccessKeyId { get; set; } = ConfigurationManager.AppSettings["AliyunAccessKeyId"];        

    public string Signature { get; set; }       

    public string SignatureMethod { get; } = "HMAC-SHA1";

    public string TimeStamp { get; set; } = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

    public string SignatureVersion { get; } = "1.0";

    public string SignatureNonce { get; } = Guid.NewGuid().ToString();
}

接下來進行最復雜的工作,計算簽名(Signature)。

由於簽名時用到Http Method與AccessKeySecret,所以給CdnRequest增加2個屬性。

private HttpMethod _httpMethod;
private string AccessKeySecret { get; set; } = ConfigurationManager.AppSettings["AccessKeySecret"];

簽名是基於請求的URL中所有參數的名稱與值,而且還要基於參數名對參數進行排序,所以我們需要增加一個Dictionary,並且將除Signature之外的7個公共參數添加到字典中。

private Dictionary<string, string> _parameters;        

private void BuildParameters()
{
    _parameters.Add(nameof(Format), Format.ToString().ToUpper());
    _parameters.Add(nameof(Version), Version);
    _parameters.Add(nameof(AccessKeyId), AccessKeyId);
    _parameters.Add(nameof(SignatureVersion), SignatureVersion);
    _parameters.Add(nameof(SignatureMethod), SignatureMethod);
    _parameters.Add(nameof(SignatureNonce), SignatureNonce);
    _parameters.Add(nameof(TimeStamp), TimeStamp);
}

接下來實現計算簽名的方法,代碼如下:

public void ComputeSignature()
{
    BuildParameters();
    var canonicalizedQueryString = string.Join("&",
        _parameters.OrderBy(x => x.Key)
        .Select(x => PercentEncode(x.Key) + "=" + PercentEncode(x.Value)));

    var stringToSign = _httpMethod.ToString().ToUpper() + "&%2F&" + PercentEncode(canonicalizedQueryString);

    var keyBytes = Encoding.UTF8.GetBytes(AccessKeySecret + "&");
    var hmac = new HMACSHA1(keyBytes);
    var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
    Signature = Convert.ToBase64String(hashBytes);
    _parameters.Add(nameof(Signature), Signature);
}

在實現這部分代碼時,遇到了一個坑,坑在PercentEncode()方法的實現中:

private string PercentEncode(string value)
{
    return UpperCaseUrlEncode(value)
        .Replace("+", "%20")
        .Replace("*", "%2A")
        .Replace("%7E", "~");
}

一開始用的不是UpperCaseUrlEncode,而是.NET類庫中的HttpUtility.UrlEncode,結果調用API時總是報”IncompleteSignature“的錯誤。

后來才知道在Java中進行Url Encode時用於編碼的字符是大寫,而C#中是小寫;阿里雲CDN API服務端用的是Java,於是我們用C#編出的碼,API服務端就不認。

再后來,在stackoverflow上找到了解決方法

private static string UpperCaseUrlEncode(string s)
{
    char[] temp = HttpUtility.UrlEncode(s).ToCharArray();
    for (int i = 0; i < temp.Length - 2; i++)
    {
        if (temp[i] == '%')
        {
            temp[i + 1] = char.ToUpper(temp[i + 1]);
            temp[i + 2] = char.ToUpper(temp[i + 2]);
        }
    }
    return new string(temp);
}

到這里就萬事俱備,只剩下生成完整的請求URL:

public string GetUrl()
{
    ComputeSignature();
    return CDN_SERVICE_BASE_ADDRESS + "?" +
        string.Join("&", _parameters.Select(x => x.Key + "=" + HttpUtility.UrlEncode(x.Value)));
}

忘了一個地方,CdnRequest的構造函數:

public CdnRequest(HttpMethod httpMethod, Dictionary<string, string> parameters)
{
    _httpMethod = httpMethod;
    _parameters = parameters;
}

最后,測試CdnRequest是否可以正常工作,測試代碼如下:

public async Task RefreshObjectCaches()
{
    var parameters = new Dictionary<string, string>()
    {
        { "Action", "RefreshObjectCaches" },
        { "ObjectPath", "http://images.cnblogs.com/logo.gif" }
    };
    var request = new CdnRequest(HttpMethod.Get, parameters);
    var url = request.GetUrl();
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStringAsync();
        Console.WriteLine(content);
    }
}

運行后,控制台輸出:

{"RefreshTaskId":"206155358","RequestId":"10F650BD-3527-4241-BB6D-D4D238AC88C7"}

這樣的輸出說明成功調用了阿里雲CDN API刷新了緩存。
搞定!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM