使用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刷新了缓存。

搞定!

C#调用阿里云CDN API刷新缓存的更多相关文章

  1. 通过AccessKey调用阿里云CDN接口刷新CDN资源案例

    通过AccessKey远程调用阿里云CDN接口,快速实现自动化集成部署. CdnService.java package com.nfky.cdn; import com.aliyuncs.Defau ...

  2. python 调用阿里云服务器api创建服务器

    首先安装阿里云SDK pip install aliyun-python-sdk-core pip install aliyun-python-sdk-ecs 可以配合jenkins传递参数 #!/u ...

  3. 突破!阿里云CDN实现毫秒级全网刷新

    通常在某网站使用了CDN节点来实现内容分发加速后,当源站内容更新的时候,CDN刷新系统会通过提交刷新请求将CDN节点上的指定缓存内容强制过期.当用户访问的时候,CDN节点将回源获取最新内容返回给用户, ...

  4. Python 调用阿里云 API 收集 ECS 数据

    #!/usr/bin/env python # coding: utf-8 # author: Wang XiaoQiang ''' 功能介绍: 1.调用阿里云API,收集所有区域 ECS 信息 2. ...

  5. 云计算之路-阿里云上:访问阿里云CDN上的图片,自动跳转到百度首页

    昨天有用户向我们反馈一篇博文(一条语句导致CPU持续100%)中的部分图片不能显示,我们的图片访问用的是阿里云CDN,原以为是某个CDN节点不稳定的问题,但在排查时发现这些图片不能显示竟然是因为请求时 ...

  6. 阿里云CDN+OSS完成图片加速

    我们使用React Native开发APP,在列表中显示图片时,列表框经常出现长时间的空白.经过稍微研究和参考其他人的经验,我们知道React Native的ListView以及后来改进版的FlatL ...

  7. 阿里云 CDN 业务基于边缘容器的云原生转型实践

    导读:本文基于边缘容器的阿里云 CDN 云原生实践, 涵盖了边缘容器的背景和趋势,边缘托管集群 ACK Managed Edge K8s(文中简称“Edge@ACK”) 的能力.架构,以及基于边缘容器 ...

  8. 什么是阿里云CDN

    阿里云内容分发网络(Content Delivery Network,简称CDN)是建立并覆盖在承载网之上,由分布在不同区域的边缘节点服务器群组成的分布式网络.阿里云CDN分担源站压力,避免网络拥塞, ...

  9. 使用Node.js调用阿里云短信的发送以及接收

    为了使用Node.js调用阿里云短信服务,我自己写了个npm包, 目前实现了: 使用Node.js调用阿里云短信服务,发送短信: 使用Node.js调用阿里云短信服务以及MNS服务,接收用户上行短信 ...

随机推荐

  1. js编码

    var url = encodeURI(encodeURI("search-keyword-"+keyword+".html")); 后台uri = Strin ...

  2. 自定义控件EditText

    public class defineEditText extends EditText { Context context; Drawable d; public defineEditText(Co ...

  3. 比较详细PHP生成静态页面教程

    一,PHP脚本与动态页面. PHP脚本是一种服务器端脚本程序,可通过嵌入等方法与HTML文件混合, 也可以类,函数封装等形式,以模板的方式对用户请求进行处理.无论以何种方式,它的基本原理是这样的.由客 ...

  4. MS SQL Server之光标、存储过程和触发器

    光标 通常数据库操作被认为是以数据集为基础的操作,但是光标被用于以记录为单位来进行操作,来获取数据库中的数据的子集.光标一般用于过程化程序里的嵌入的SQL语句. 对光标的定义如下: DECLARE C ...

  5. SQL镜像资料

    使用数据库镜像端点证书 (Transact-SQL):https://msdn.microsoft.com/zh-cn/library/ms191477.aspx 允许数据库镜像端点将证书用于入站连接 ...

  6. js 获取 通过 ”?“ 或者 ”&“ url 传过来参数值

    请先 引用 jQuery的js <script> String.prototype.GetValue=function(para) { var reg = new RegExp(" ...

  7. union all 里面的order by

    例1: SELECT 1 order2 FROM dual union all SELECT 3 order2 FROM dual union all SELECT 2 order1 FROM dua ...

  8. Sql 查找整个数据库中的字符串

    --存储过程 CREATE PROCEDURE [dbo].[SP_FindValueInDB] ( @value VARCHAR() ) AS BEGIN SET NOCOUNT ON; DECLA ...

  9. java去中文

    java 去中文 package a.b; public class TrimCNTool { public static boolean checkCNChar(char oneChar) { if ...

  10. linux磁盘限额和进阶文件系统的管理 quota RAID LVM

    概念: Quota 的一般用途: 针对 WWW server ,例如:每个人的网页空间的容量限制! 针对 mail server,例如:每个人的邮件空间限制. 针对 file server,例如:每个 ...