zoukankan      html  css  js  c++  java
  • 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刷新了缓存。
    搞定!

  • 相关阅读:
    ASCII对照表
    Python学习记录-3-简明Python教程-数据结构
    Python学习记录-2
    Python新手容易遇到的问题
    python学习问题之-编码
    同步与异步的概念(转自http://blog.chinaunix.net/uid-21411227-id-1826898.html)
    Objective-c学习三
    挺有意思的人体时钟代码(转自http://ziren.org/tools/hone-hone-clock.html)
    int ,long , long long类型的范围
    css3选择器使用例子
  • 原文地址:https://www.cnblogs.com/dudu/p/5141814.html
Copyright © 2011-2022 走看看