PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/Daily_20080505/Facebook/FacebookRequest.cs

#
C# | 174 lines | 118 code | 27 blank | 29 comment | 6 complexity | 0a13ddfc4f06378add0912b11654c744 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Security.Cryptography;
  8. using System.Text;
  9. using System.Web;
  10. namespace Facebook
  11. {
  12. /// <summary>Makes a request to a Facebook API method.</summary>
  13. [Serializable]
  14. public class FacebookRequest
  15. {
  16. /// <summary>The URL of the Facebook API REST application.</summary>
  17. internal const String FACEBOOK_REST_URL = "https://api.facebook.com/restserver.php";
  18. /// <summary>Intializes an instance of <see cref="FacebookRequest" /> for the specified <paramref name="methodName"/>.</summary>
  19. /// <param name="facebookContext">A reference to the <see cref="IFacebookContext" /> object the request is being made for.</param>
  20. /// <param name="methodName">The name of the API method.</param>
  21. /// <param name="args">A <see cref="IDictionary{String,Object}" /> object containing arguments to be passed with the method call.</param>
  22. /// <param name="excludedArgs">An array of <see cref="String" /> values specifying arguments that should be left out of the API method call.</param>
  23. public FacebookRequest(IFacebookContext facebookContext, String methodName, IDictionary<String, Object> args, params String[] excludedArgs)
  24. {
  25. this.Context = facebookContext;
  26. this.MethodName = methodName;
  27. this.Args = args;
  28. this.ExcludedArgs = excludedArgs;
  29. }
  30. /// <summary>Gets a reference to the <see cref="IFacebookContext" /> the request if being made for.</summary>
  31. public IFacebookContext Context { get; private set; }
  32. /// <summary>Gets the name of the method being called.</summary>
  33. public String MethodName { get; internal set; }
  34. /// <summary>Gets a dictionary of arguments that will be inluded in the request.</summary>
  35. public IDictionary<String, Object> Args { get; internal set; }
  36. /// <summary>Gets or sets the array of <see cref="String" /> values specifying arguments that should be left out of the API method call.</summary>
  37. public String[] ExcludedArgs { get; set; }
  38. /// <summary>Executes the request.</summary>
  39. /// <typeparam name="TValue">The expected return type of the method call.</typeparam>
  40. /// <param name="timeout">The amount of time, in milliseconds, the request will be active before timing out.</param>
  41. /// <returns>A reference to a <see cref="FacebookResponse{TValue}" /> object that will contain the value of the response.</returns>
  42. internal FacebookResponse<TValue> Execute<TValue>(Int32 timeout)
  43. {
  44. try
  45. {
  46. var request = this.CreateWebRequest(timeout);
  47. var response = (HttpWebResponse)request.GetResponse();
  48. return FacebookRequest.ProcessResponse<TValue>(response);
  49. }
  50. catch (Exception ex) { return new FacebookResponse<TValue>(ex); }
  51. }
  52. /// <summary>Creates an <see cref="HttpWebRequest" /> object for the request.</summary>
  53. /// <param name="timeout">The amount of time, in milliseconds, the request will be active before timing out.</param>
  54. /// <returns>An <see cref="HttpWebRequest" /> object for the request.</returns>
  55. internal HttpWebRequest CreateWebRequest(Int32 timeout)
  56. {
  57. var content = this.GenerateRequestContent();
  58. var request = (HttpWebRequest)WebRequest.Create(FacebookRequest.FACEBOOK_REST_URL);
  59. request.Timeout = timeout;
  60. request.Method = "POST";
  61. request.ContentType = "application/x-www-form-urlencoded";
  62. request.ContentLength = content.Length;
  63. using (var stream = request.GetRequestStream())
  64. {
  65. using (var writer = new StreamWriter(stream))
  66. {
  67. writer.Write(content.ToString());
  68. writer.Close();
  69. }
  70. }
  71. return request;
  72. }
  73. /// <summary>Generates a <see cref="String" /> containing the content stream for the request.</summary>
  74. /// <returns>A <see cref="String" /> containing the content stream for the request.</returns>
  75. internal String GenerateRequestContent()
  76. {
  77. var args = new Dictionary<String, Object>(this.Args ?? new Dictionary<String, Object>());
  78. foreach (var excluded in this.ExcludedArgs) args.Remove(excluded);
  79. args.Add("method", this.MethodName);
  80. args.Add("api_key", this.Context.ApiKey);
  81. args.Add("v", this.Context.Version);
  82. if (this.Context.HasSession && !this.ExcludedArgs.Contains("session_key"))
  83. {
  84. //args.Add("ss", "1");
  85. args.Add("session_key", this.Context.Session.SessionKey);
  86. args.Add("call_id", DateTime.Now.Ticks);
  87. }
  88. else
  89. {
  90. args.Add("ss", "0");
  91. args.Add("session_key", String.Empty);
  92. args.Add("call_id", String.Empty);
  93. }
  94. var sig = FacebookRequest.ComputeSignature(this.Context.Secret, args);
  95. args.Add("sig", sig);
  96. var content = new StringBuilder();
  97. foreach (var arg in args)
  98. {
  99. Object value = null;
  100. IEnumerable col = null;
  101. if (arg.Value.TryMakeEnumerable(out col)) value = col.ToDelimitedString(',');
  102. else value = arg.Value;
  103. content.AppendFormat("{0}{1}={2}", content.Length > 0 ? "&" : String.Empty, arg.Key, HttpUtility.UrlEncode(value.ToString()));
  104. }
  105. return content.ToString();
  106. }
  107. /// <summary>Computes the signature of a request passing the specified <paramref name="arguments"/>.</summary>
  108. /// <param name="secret">The application secret.</param>
  109. /// <param name="arguments">The list of arguments being passed into the method request.</param>
  110. /// <returns>A <see cref="String" /> containing a request signature generated according to the documentation provided by Facebook in the article <a href="http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application">How Facebook Authenticates Your Application</a>.</returns>
  111. internal static String ComputeSignature(String secret, IDictionary<String, Object> arguments)
  112. {
  113. var sortedArgs = arguments.OrderBy(arg => arg.Key);
  114. var seedBuilder = new StringBuilder();
  115. foreach (var arg in sortedArgs)
  116. {
  117. Object value = null;
  118. IEnumerable col = null;
  119. if (arg.Value.TryMakeEnumerable(out col)) value = col.ToDelimitedString(',');
  120. else value = arg.Value;
  121. seedBuilder.AppendFormat("{0}={1}", arg.Key, value);
  122. }
  123. seedBuilder.Append(secret);
  124. var seed = seedBuilder.ToString();
  125. var seedBytes = Encoding.ASCII.GetBytes(seed);
  126. var sig = MD5.Create().ComputeHashString(seed);
  127. return sig.ToLower();
  128. }
  129. /// <summary>Processes the <see cref="HttpWebResponse" /> object generated by the request and returns a <see cref="FacebookResponse{TValue}" /> object containing the result.</summary>
  130. /// <typeparam name="TValue">The expected return type of the method call.</typeparam>
  131. /// <param name="response">A <see cref="HttpWebResponse" /> object.</param>
  132. /// <returns>A <see cref="FacebookResponse{TValue}" /> object containing the result of the API method call.</returns>
  133. internal static FacebookResponse<TValue> ProcessResponse<TValue>(HttpWebResponse response)
  134. {
  135. String responseContent = null;
  136. using (var stream = response.GetResponseStream())
  137. {
  138. using (var reader = new StreamReader(stream))
  139. {
  140. responseContent = reader.ReadToEnd();
  141. reader.Close();
  142. }
  143. }
  144. return new FacebookResponse<TValue>(responseContent);
  145. }
  146. }
  147. }