I have asked a couple of questions about serializing dictionaries and I realized I may not even be using Newtonsoft.JSON in my .ASMX responses!
I have a JSON-emitting webservice class in an .asmx file:
[WebService(Namespace = "www.???.com")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class ApplicationService : JSONWebServiceBase
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public DateTime GetX()
{
return DateTime.Now;
}
}
To check my JSON, I returned a DateTime property, and using simon_weaver's test this date showed up on the client as
{"\/Date(1355627201572)\/"}
instead of
{"2012-12-15T19:07:03.5247384-08:00"}
which tells me the Microsoft serializer is being used and not Newtonsoft.JSON. How do I tell ASP.NET I want to use Newtonsoft?
Check following may work for your
var d = new Date(1245398693390);
var formattedDate = d.getDate() + "-" + (d.getMonth() + 1) + "-" + d.getFullYear();
var hours = (d.getHours() < 10) ? "0" + d.getHours() : d.getHours();
var minutes = (d.getMinutes() < 10) ? "0" + d.getMinutes() : d.getMinutes();
var formattedTime = hours + ":" + minutes;formattedDate = formattedDate + " " + formattedTime;
Related
I have a web service .asmx that I call from an android mobile application. it return a random number to the android application, and send the same number using sms.
My problem that some times the web service is executed more than one time in the same time and returning the same random number. so the sms is sent more than one time all in one request.
I have two files one is the one I call from my app getRandomNumber.asmx and the second is getRandomNumber.cs the file that generate the number, save to the database and send it by sms.
so i put a log file in both files and waited to the duplicate to show.
this is from a log file I made to track this issue:
Time: 2014/05/24 06:41:46 - user: 610410 - random: Going IN
Time: 2014/05/24 06:41:46 - user: 610410 - random: Going IN
Time: 2014/05/24 06:41:46 - user: 610410 - random: Going IN
Time: 2014/05/24 06:41:46 - user: 610410 - random: Going IN
Time: 2014/05/24 06:41:49 - user: 610410 - random: 853967
Time: 2014/05/24 06:41:49 - user: 610410 - random: 853967
Time: 2014/05/24 06:41:49 - user: 610410 - random: 853967
Time: 2014/05/24 06:41:49 - user: 610410 - random: 853967
what is the cause and how to solve it.
UPDATE: Code added
getRandomNumber.asmx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Web.Script.Serialization;
using OracleDataAccessLayer;
namespace MobileWebServices
{
[WebService(Namespace = "http://www.example.com")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class getRandomNumber : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public string sendRandomNumber(int UserNumber)
{
Logger.logError("Time: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " - user: " + UserNumber + " - random: Going IN");
return Newtonsoft.Json.JsonConvert.SerializeObject(new getRandomNumber().sendRandomNumber(UserNumber));
}
}
}
getRandomNumber.cs
using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Text;
using System.IO;
namespace OracleDataAccessLayer
{
public class getRandomNumber : OracleDataAccessLayer.MobileDataAccess.OracleCommandLibrary
{
string sql = "";
public getRandomNumber() { }
string GeneratePasscode(int PasscodeLength)
{
string _allowedChars = "123456789";
Random randNum = new Random();
char[] chars = new char[PasscodeLength];
int allowedCharCount = _allowedChars.Length;
for (int i = 0; i < PasscodeLength; i++)
{
chars[i] = _allowedChars[(int)((_allowedChars.Length) * randNum.NextDouble())];
}
return new string(chars).PadRight(PasscodeLength, '0').ToString();
}
public string sendRandomNumber(int parUserNumber)
{
string random = "0";
string mobile="";
try
{
string randomNum = GeneratePasscode(6);
Logger.logError("Time: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " - user: " + parUserNumber + " - random: " + randomNum);
//select the mobile number and the language
sql = "SELECT C.COUNTRY_LANG, MOBILE " +
"FROM COUNTRY_MASTER_TABLE C " +
"JOIN USER_REG_MASTER U ON " +
"C.COUNTRY_CODE = U.NATIONALITY_NO " +
"WHERE U.UNO = " + parUserNumber;
DataTable lang = Retrievedata(sql);
if (lang.Rows.Count > 0)
{
sql = "INSERT INTO SMS_SEND " +
"(SMS_ID, USER_NUMBER, SMS_DATE, RANDOM, DEVICE_TYPE) VALUES " +
"(SEQ_RANDOM.NEXTVAL, " + parUSERNumber + ", SYSDATE, " + randomNum + ", 1)";
if (ExecuteTransactions(sql) > 0)
{
mobile = lang.Rows[0].ItemArray[1].ToString();
// check the lang Arabic, Non Arabic
// if arabic convert to hex
if (lang.Rows[0].ItemArray[0].ToString() == "A")
{
messageOrg = "الرجاء استخدام الرمز " + randomNum;
Byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(messageOrg);
StringBuilder sbBytes = new StringBuilder(stringBytes.Length * 2);
foreach (byte bb in stringBytes)
{
sbBytes.AppendFormat("%{0:X2}", bb);
}
message = sbBytes.ToString();
messageHex = message;
}
else
{
// If english do nothing
messageOrg = "Please use this code: " + randomNum;
message = "Please use this code: " + randomNum;
}
// add the message and mobile number to SMS service provider URL and create HttpWebRequest
string url = "http://www.example.com/Send.aspx?MOBILENO=" + mobile + "&MESSAGE=" + message + "&rest of prams";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
// execute request and get response back
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
smsSequence = reader.ReadToEnd();
reader.Close();
dataStream.Close();
}
// check if success get the response and check id number and insert into SMS_SENTBOX for logging
int success = 0;
if (smsSequence.Substring(0, 3) == "+OK")
//if (Int32.TryParse(smsSequence.Substring(7, smsSequence.Length - 7), out success))
{
random = randomNum;
sql = "INSERT INTO SMS_SENTBOX " +
"(SMS_SENT_SEQUENCE, USER_NUMBER, EVENT_DATE, EVENT_TIME, USER_MOBILE, EVENT_TYPE, SENT_DATE, SMS_TEXT, SMS_TEXT_DUMP)" +
"VALUES " +
"(1, " + parUserNumber + ", SYSDATE, TO_CHAR(SYSDATE, 'HHMI'), " + mobile + ", 'random', SYSDATE, '" + messageOrg + "', '" + messageHex + "')";
try
{
ExecuteTransactions(sql);
}
catch (Exception e)
{
}
}
}
}
}
catch (Exception e)
{
Logger.logError(e.ToString() + "\n");
throw e;
}
return random;
/**/
}
}
}
thankyou
From your description, I don't quite understand whether you have a single problem (the same random number is generated multiple times) or two problems (same random number and request is sent multiple times).
Anyhow, the source of your problem is described in Microsoft's documentation about the Random() constructor:
The default seed value is derived from the system clock and has finite
resolution. As a result, different Random objects that are created in
close succession by a call to the default constructor will have
identical default seed values and, therefore, will produce identical
sets of random numbers.
To fix it, create a single static instance of Random:
static Random random = new Random();
....
string GeneratePasscode(int PasscodeLength)
{
string _allowedChars = "123456789";
char[] chars = new char[PasscodeLength];
int allowedCharCount = _allowedChars.Length;
for (int i = 0; i < PasscodeLength; i++)
{
chars[i] = _allowedChars[(int)((_allowedChars.Length) * random.NextDouble())];
}
return new string(chars).PadRight(PasscodeLength, '0').ToString();
}
I use PageMethods object to call a codebehind method in asp.net. I can send and receive parameters like string, int etc. But i must send a javaScript object to codeBehind. How can i parse the parameter and get data from codeBehind? I think i must use JSON parser but i wonder if there is an easy way, or if .net framework has Json parser (or like JSON) ?
<script type="text/javascript" language="javascript">
function test(idParam, nameParam) {
var jsonObj = { id: idParam, name: nameParam };
PageMethods.testMethod(jsonObj,
function (result) { alert(result) });
}
</script>
[WebMethod()]
public static string testMethod(object param)
{
int id = 1;//I must parse param and get id
string name = "hakan"; //I must parse param and get name
return "Id:" + id + "\n" + "Name:" + name + "\n" + "Date:" + DateTime.Now;
}
Try this (you can add System.Collections.Generic as a using clause to clean it up more):
[WebMethod()]
public static string testMethod(object param)
{
System.Collections.Generic.Dictionary<String, Object> Collection;
Collection = param as System.Collections.Generic.Dictionary<String, Object>;
int id = (int) Collection["id"];
string name = Collection["name"] as String;
return "Id:" + id + "\n" + "Name:" + name + "\n" + "Date:" + DateTime.Now;
}
[Edit] Even simpler:
// using System.Collections.Generic;
[WebMethod()]
public static string testMethod(Dictionary<String, Object> Collection)
{
int id = (int) Collection["id"];
string name = Collection["name"] as String;
return "Id:" + id + "\n" + "Name:" + name + "\n" + "Date:" + DateTime.Now;
}
I have implement a simple file-based custom OutputCacheProvider based on samples i found on the Internet.
The code follows:
using System;
using System.Configuration;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace SimpleCachedProvider
{
public class FileCacheProvider : OutputCacheProvider {
private string _cachePath;
void WriteToFile(String filename, String contents) {
FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter w = new StreamWriter(fs, System.Text.Encoding.GetEncoding(1253));
w.BaseStream.Seek(0, SeekOrigin.Begin);
w.BaseStream.SetLength(0);
w.Write(contents);
w.Flush();
w.Close();
}
void AppendToFile(String filename, String contents) {
if (contents.ToLower().IndexOf("ss2.aspx") >= 0 || contents.ToLower().IndexOf("default.aspx") >= 0) {
FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter w = new StreamWriter(fs, System.Text.Encoding.GetEncoding(1253));
w.BaseStream.Seek(0, SeekOrigin.End);
w.Write(contents);
w.Flush();
w.Close();
}
}
private string CachePath {
get {
if (!string.IsNullOrEmpty(_cachePath))
return _cachePath;
_cachePath = ConfigurationManager.AppSettings["OutputCachePath"];
var context = HttpContext.Current;
if (context != null) {
_cachePath = context.Server.MapPath(_cachePath);
if (!_cachePath.EndsWith("\\"))
_cachePath += "\\";
}
return _cachePath;
}
}
public override object Add(string key, object entry, DateTime utcExpiry) {
var path = GetPathFromKey(key);
AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ")\r\n");
if (File.Exists(path)) {
AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") already exists. Will be returned.\r\n");
return entry;
}
AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") does not exists. Will be created.\r\n");
using (var file = File.OpenWrite(path)) {
var item = new CacheItem { Expires = utcExpiry, Item = entry };
var formatter = new BinaryFormatter();
formatter.Serialize(file, item);
AppendToFile(CachePath + "info.txt", "ADD: " + key + " (" + path + ") saved to disk.\r\n");
}
return entry;
}
public override void Set(string key, object entry, DateTime utcExpiry) {
var item = new CacheItem { Expires = utcExpiry, Item = entry };
var path = GetPathFromKey(key);
AppendToFile(CachePath + "info.txt", "Set: " + key + " (" + path + ") requested.\r\n");
using (var file = File.OpenWrite(path)) {
var formatter = new BinaryFormatter();
formatter.Serialize(file, item);
AppendToFile(CachePath + "info.txt", "Set: " + key + " (" + path + "): " + utcExpiry.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss") + " saved to disk.\r\n");
}
}
public override object Get(string key) {
var path = GetPathFromKey(key);
AppendToFile(CachePath + "info.txt", "Get: Querying " + key + " (" + path + ")\r\n");
if (!File.Exists(path)) {
AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") not found.\r\n");
return null;
}
CacheItem item = null;
using (var file = File.OpenRead(path)) {
var formatter = new BinaryFormatter();
item = (CacheItem)formatter.Deserialize(file);
AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") retrieved.\r\n");
}
if (item == null || item.Expires <= DateTime.Now.ToUniversalTime()) {
AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") deleted due to expiration.\r\n");
Remove(key);
return null;
}
AppendToFile(CachePath + "info.txt", "Get: " + key + " (" + path + ") retrieved and used\r\n");
return item.Item;
}
public override void Remove(string key) {
var path = GetPathFromKey(key);
AppendToFile(CachePath + "info.txt", "Remove: " + key + " (" + path + ") requested.\r\n");
if (File.Exists(path)) {
AppendToFile(CachePath + "info.txt", "Remove: " + key + " (" + path + ") executed.\r\n");
File.Delete(path);
}
}
private string GetPathFromKey(string key) {
return CachePath + MD5(key) + ".txt";
}
private string MD5(string s) {
MD5CryptoServiceProvider provider;
provider = new MD5CryptoServiceProvider();
byte[] bytes = Encoding.UTF8.GetBytes(s);
StringBuilder builder = new StringBuilder();
bytes = provider.ComputeHash(bytes);
foreach (byte b in bytes)
builder.Append(b.ToString("x2").ToLower());
return builder.ToString();
}
}
}
I have then created an .aspx with the header
<%# OutputCache Duration="3600" Location="Server" VaryByParam="*" %>
I have changed the default output cache provider to my web.config to mine.
The strange behavior is that the page is not cached. Instead this is a sample output from my debugging information. It seems that:
The page is retrieved from tha cache and sent back to ASP.Net
Right after that ASP.Net calls the Remove() method to my page
Finally ASP.Net calls Set() and the page is updated - no effective caching
Get: Querying a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt)
Get: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) retrieved.
Get: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) retrieved and used
Get: Querying a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt)
Get: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) retrieved.
Get: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) retrieved and used
Remove: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) requested.
Remove: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) executed.
ADD: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt)
ADD: a2/ss2.aspx (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\7394fd15241e5b7f5c437ddf28dcd0e5.txt) already exists. Will be returned.
Set: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt) requested.
Set: a2/ss2.aspxHQFCNmycustom2VDE (C:\eShopKey\ASP.Net\Shops\myshoe_dev\Cache\3e72454ab3f36e4cfe3964e5063be622.txt): 30/05/2012 15:07:27 saved to disk.
So my questions:
Why ASP.Net keeps invalidating my page?
When Remove() and Set() methods are called by ASP.Net? I have not found any info regarding that.
If i rename the page and use this variation caching works! This is totally weird.
Note that if i use the default ASP.Net outputcacheprovider caching works as expected.
I found what is going on but unable to fix it:
Let's say i open the page: http://www.mydomain.com/mypage.aspx?param1=1
ASP.Net sends 2 consecutive GET requests to my OutputCacheProvider:
one for the page mypage.aspx
another for the same page but with the querystring parameters attached
It seems to me that the first request is somehow related with the second one, like a header.
As soon as i call consecutively the same page, with the same querystring, caching working as expected.
If i call next the page: http://www.mydomain.com/mypage.aspx?param1=2
then the same, 2 step GET sequence, is initialized. The ASP.Net sends 2 GET requests, one for the page without parameters and one with parameters.
The first GET request (for the page without parameters) is then found on the cache and returned back to ASP.Net. But somehow is unrelated with the second one. It is related to the first variation of the call (param1=1).
So, nevertheless if the second request has been cached before, ASP.Net thinks that the cached page is invalid and ask again for add / set.
To summarize it seems that you can have just one variation of the page to the cache at a given moment. All previous cahed variations will be invalidated as the page will be called again with other parameters.
There is no way to check what the first GET request is related to as ASP.NET uses the same key to retrieve it.
So my new questions:
Why ASP.Net sends 2 requests for every page to the custom output cache provider? Does anybody knows?
How i can overcome this strange behavior?
Is the AspNetInternalProvider has the same behavior?
I found the solution! The problem was on Add method. It has to be written on all providers like below:
public override object Add(string key, object entry, DateTime utcExpiry) {
String vKey = TransformKey(key);
object res = Get(key);
if (res == null) {
Set(key, entry, utcExpiry);
return entry;
}
return res;
}
The TransformKey method just returns a safe string (string without bad characters) based on key (for example the MD5 hash of the key). Look for an implementation on my first posted code.
the first request returns an object System.Web.Caching.CachedVary, and second request returns System.Web.Caching.OutputCacheEntry. According to the name of object, the first one is for OutputCache, and the second one is for the data of page.
if you have any questions, pls send email to shengzhengshan#hotmail.com
Hope it can help you!
Amir Sheng
I am trying to write test cases for our AJAX calls to our API. Doing a simply web request and response. My question is with regard to the response. Is there a simpler way to pull out the response JSON values? Is the best way to do this sort of thing? I know we could us JQuery, but wanted to use Microsoft Testing framework.
[TestMethod]
public void TestMethod1()
{
string brand = "KEWL";
string BRAND = "";
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://203.135.xx.138:4040/api/v1/subscriptions/signup.format");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = #"{" +
" 'api_key': '91230D10-247C-11E1-83FF-9B9C4824019B'," +
" 'phone': '12122639043', " +
" 'dob': '11231954', " +
" 'subscriptions': [ " +
" {" +
" 'Brand':'" + brand + "', " +
" 'campaign':'BTLNDN', " +
" 'groups':[" +
" {" +
" 'group': 'BTLALL'," +
" 'subscribed':true" +
" } " +
" ]," +
" 'lang': 'en' " +
" }" +
" ] " +
" }";
streamWriter.Write(json);
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, dynamic> dc = serializer.Deserialize<Dictionary<string, dynamic>>(responseText);
var kev = dc;
foreach (var key1 in dc.Keys)
{
var value3 = dc["ReturnData"]["subscriptions"];
BRAND = value3[0]["brand"];
// var groups = value3[0]["groups"];
}
}
Assert.AreEqual(brand, BRAND);
}
The idea of unit testing ASP.NET MVC methods is that you can run the test without using any Http request or respond functionality.
Suppose you have the following method:
public class MyController : Controller
{
public ActionResult MyAjax()
{
return Json(new { Test = "Test" });
}
}
You can test it with this code:
[TestMethod]
public void MyTest()
{
MyControllercontroller = new MyController();
JsonResult json = controller.MyAjax() as JsonResult;
Assert.IsNotNull(json);
dynamic data = json.Data;
Assert.AreEqual("Test", data.Test);
}
To use the dynamic keyword you have to make sure that your test project can see the internals of your web project (this is because anonymous types are declared internal). You can do this by adding: [assembly: InternalsVisibleTo("YourTestProject")] to the AssemblyInfo.cs file of your web project.
I am not sure how to send a request using ASP.Net to Amazon CloudFront to invalidate an object.
The details are here http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/index.html?Invalidation.html
but I am not sure how to implement this in ASP.Net.
The accepted answer no longer works as of the latest version of the AWS SDK for .NET (1.5.8.0). This should do the trick:
using Amazon;
using Amazon.CloudFront.Model;
...
var client = AWSClientFactory.CreateAmazonCloudFrontClient(accessKey, secretKey);
client.CreateInvalidation(new CreateInvalidationRequest {
DistributionId = distributionID,
InvalidationBatch = new InvalidationBatch {
Paths = new Paths {
Quantity = arrayofpaths.Length,
Items = arrayofpaths.ToList()
},
CallerReference = DateTime.Now.Ticks.ToString()
}
});
Got it working, here it is if anyone else finds it useful.
public static void InvalidateContent(string distributionId, string fileName)
{
string httpDate = Helpers.GetHttpDate();
ASCIIEncoding encoding = new ASCIIEncoding();
string postData = #"<InvalidationBatch>" +
" <Path>/" + fileName + "</Path>" +
" <CallerReference>" + httpDate + "</CallerReference>" +
"</InvalidationBatch>";
byte[] data = encoding.GetBytes(postData);
// Prepare web request...
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://cloudfront.amazonaws.com/2010-08-01/distribution/" + distributionId + "/invalidation");
webRequest.Method = "POST";
webRequest.ContentType = "text/xml";
webRequest.Headers.Add("x-amz-date", httpDate);
Encoding ae = new UTF8Encoding();
HMACSHA1 signature = new HMACSHA1(ae.GetBytes(GlobalSettings.AWSSecretAccessKey.ToCharArray()));
string b64 = Convert.ToBase64String(signature.ComputeHash(ae.GetBytes(webRequest.Headers["x-amz-date"].ToCharArray())));
webRequest.Headers.Add(HttpRequestHeader.Authorization, "AWS" + " " + GlobalSettings.AWSAccessKeyId + ":" + b64);
webRequest.ContentLength = data.Length;
Stream newStream = webRequest.GetRequestStream();
// Send the data.
newStream.Write(data, 0, data.Length);
newStream.Close();
}
/// <summary>
/// Gets a proper HTTP date
/// </summary>
public static string GetHttpDate()
{
// Setting the Culture will ensure we get a proper HTTP Date.
string date = System.DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss ", System.Globalization.CultureInfo.InvariantCulture) + "GMT";
return date;
}
Here's a python version of the above, if anyone finds it useful
from datetime import datetime
import urllib2, base64, hmac, hashlib
def getHTTPDate():
return datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S UTC")
def submitInvalidationRequest(fileName,distributionId):
url = "https://cloudfront.amazonaws.com/2010-08-01/distribution/" + distributionId + "/invalidation"
httpDate = getHTTPDate();
postData = "<InvalidationBatch>" +"<Path>/" + fileName + "</Path>" +"<CallerReference>" + httpDate + "</CallerReference>" +"</InvalidationBatch>";
sig = hmac.new(AWSSecretAccessKey, unicode(httpDate), hashlib.sha1)
headers = {"ContentType": "text/xml",
"x-amz-date": httpDate,
"Authorization":"AWS " + AWSAccessKeyId + ":" + base64.b64encode( sig.digest() )}
req = urllib2.Request(url,postData,headers)
return urllib2.urlopen(req).read()
using the AWSSDK .net api wrapper from amazon makes this task even easier.
using Amazon.CloudFront.Model;
...
var client = Amazon.AWSClientFactory.CreateAmazonCloudFrontClient(ConfigurationManager.AppSettings["Aws.AccessKey"],
ConfigurationManager.AppSettings["Aws.SecretKey"]);
var request = new PostInvalidationRequest();
request.DistributionId = ConfigurationManager.AppSettings["Cdn.DistributionId"];
request.InvalidationBatch = new InvalidationBatch();
request.InvalidationBatch.CallerReference = new Guid().ToString();
request.InvalidationBatch.Paths = PathsInput.Text.Split(new[]{'\n','\r'},StringSplitOptions.RemoveEmptyEntries).ToList();
var response = client.PostInvalidation(request);
Here's perl:
use warnings;
use strict;
use HTTP::Date;
use Digest::SHA qw(hmac_sha1);
use LWP::UserAgent;
use MIME::Base64;
use Encode qw(encode_utf8);
#ARGV == 4 || die "usage: $0 url distribution_id accesskey secretkey\n";
my $invalid_url = $ARGV[0];
my $distribution_id = $ARGV[1];
my $accesskey = $ARGV[2];
my $secretkey = $ARGV[3];
my $url = "https://cloudfront.amazonaws.com/2010-11-01/distribution/$distribution_id/invalidation";
my $date = time2str;
my $post_data = <<HERE;
<?xml version="1.0" encoding="UTF-8"?>
<InvalidationBatch>
<Path>$invalid_url</Path>
<CallerReference>$date</CallerReference>
</InvalidationBatch>
HERE
my $sig = encode_base64(hmac_sha1(encode_utf8($date),encode_utf8($secretkey)));
my $browser = LWP::UserAgent->new;
my $res = $browser->post($url,
"Content" => $post_data,
"ContentType" => "text/xml",
"x-amz-date" => $date,
"Authorization" => "AWS $accesskey:$sig");
print $res->status_line, "\n", $res->content;