chore: 规范 http 请求 headers

oneRain 2019-08-09 12:27:35 +08:00
parent 265a34aa9b
commit f2ec04a331
4 changed files with 91 additions and 100 deletions

View File

@ -25,7 +25,14 @@ namespace LeanCloud.Storage.Internal
} }
public Dictionary<string, string> Headers { public Dictionary<string, string> Headers {
get; set; get {
if (AVUser.CurrentUser != null) {
return new Dictionary<string, string> {
{ "X-LC-Session", AVUser.CurrentUser.SessionToken }
};
}
return null;
}
} }
public object Content { public object Content {

View File

@ -6,6 +6,7 @@ using System.Net.Http.Headers;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Text; using System.Text;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal { namespace LeanCloud.Storage.Internal {
@ -13,9 +14,35 @@ namespace LeanCloud.Storage.Internal {
/// Command Runner. /// Command Runner.
/// </summary> /// </summary>
public class AVCommandRunner { public class AVCommandRunner {
public const string APPLICATION_JSON = "application/json"; const string APPLICATION_JSON = "application/json";
const string USE_PRODUCTION = "1";
const string USE_DEVELOPMENT = "0";
private readonly System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient(); private readonly System.Net.Http.HttpClient httpClient;
public AVCommandRunner() {
httpClient = new System.Net.Http.HttpClient();
ProductHeaderValue product = new ProductHeaderValue(AVClient.Name, AVClient.Version);
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(APPLICATION_JSON));
var conf = AVClient.CurrentConfiguration;
// App ID
httpClient.DefaultRequestHeaders.Add("X-LC-Id", conf.ApplicationId);
// App Signature
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
if (!string.IsNullOrEmpty(conf.MasterKey) && AVClient.UseMasterKey) {
string sign = MD5.GetMd5String(timestamp + conf.MasterKey);
httpClient.DefaultRequestHeaders.Add("X-LC-Sign", $"{sign},{timestamp},master");
} else {
string sign = MD5.GetMd5String(timestamp + conf.ApplicationKey);
httpClient.DefaultRequestHeaders.Add("X-LC-Sign", $"{sign},{timestamp}");
}
// TODO Session
// Production
httpClient.DefaultRequestHeaders.Add("X-LC-Prod", AVClient.UseProduction ? USE_PRODUCTION : USE_DEVELOPMENT);
}
/// <summary> /// <summary>
/// ///
@ -30,21 +57,22 @@ namespace LeanCloud.Storage.Internal {
IProgress<AVDownloadProgressEventArgs> downloadProgress = null, IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default) { CancellationToken cancellationToken = default) {
string content = JsonConvert.SerializeObject(command.Content);
var request = new HttpRequestMessage { var request = new HttpRequestMessage {
RequestUri = command.Uri, RequestUri = command.Uri,
Method = command.Method, Method = command.Method,
Content = new StringContent(JsonConvert.SerializeObject(command.Content)) Content = new StringContent(content)
}; };
var headers = GetHeadersAsync(); request.Content.Headers.ContentType = new MediaTypeHeaderValue(APPLICATION_JSON);
foreach (var header in headers) { // 特殊 Headers
if (!string.IsNullOrEmpty(header.Value)) { if (command.Headers != null) {
foreach (KeyValuePair<string, string> header in command.Headers) {
request.Headers.Add(header.Key, header.Value); request.Headers.Add(header.Key, header.Value);
} }
} }
request.Content.Headers.ContentType = new MediaTypeHeaderValue(APPLICATION_JSON); PrintRequest(httpClient, request, content);
PrintRequest(command, headers);
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
request.Dispose(); request.Dispose();
@ -87,38 +115,22 @@ namespace LeanCloud.Storage.Internal {
return new Tuple<HttpStatusCode, T>(responseCode, default); return new Tuple<HttpStatusCode, T>(responseCode, default);
} }
private const string revocableSessionTokenTrueValue = "1"; static void PrintRequest(System.Net.Http.HttpClient client, HttpRequestMessage request, string content) {
Dictionary<string, string> GetHeadersAsync() {
var headers = new Dictionary<string, string>();
var installationId = AVPlugins.Instance.InstallationIdController.Get();
headers.Add("X-LC-Installation-Id", installationId);
var conf = AVClient.CurrentConfiguration;
headers.Add("X-LC-Id", conf.ApplicationId);
long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
if (!string.IsNullOrEmpty(conf.MasterKey) && AVClient.UseMasterKey) {
string sign = MD5.GetMd5String(timestamp + conf.MasterKey);
headers.Add("X-LC-Sign", $"{sign},{timestamp},master");
} else {
string sign = MD5.GetMd5String(timestamp + conf.ApplicationKey);
headers.Add("X-LC-Sign", $"{sign},{timestamp}");
}
// TODO 重新设计版本号
headers.Add("X-LC-Client-Version", AVClient.VersionString);
headers.Add("X-LC-App-Build-Version", conf.VersionInfo.BuildVersion);
headers.Add("X-LC-App-Display-Version", conf.VersionInfo.DisplayVersion);
headers.Add("X-LC-OS-Version", conf.VersionInfo.OSVersion);
headers.Add("X-LeanCloud-Revocable-Session", revocableSessionTokenTrueValue);
return headers;
}
static void PrintRequest(AVCommand request, Dictionary<string, string> headers) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Request Start ==="); sb.AppendLine("=== HTTP Request Start ===");
sb.AppendLine($"URL: {request.Uri}"); sb.AppendLine($"URL: {request.RequestUri}");
sb.AppendLine($"Method: {request.Method}"); sb.AppendLine($"Method: {request.Method}");
sb.AppendLine($"Headers: {JsonConvert.SerializeObject(headers)}"); sb.AppendLine($"Headers: ");
sb.AppendLine($"Content: {JsonConvert.SerializeObject(request.Content)}"); foreach (var header in client.DefaultRequestHeaders) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
foreach (var header in request.Headers) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
foreach (var header in request.Content.Headers) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
sb.AppendLine($"Content: {content}");
sb.AppendLine("=== HTTP Request End ==="); sb.AppendLine("=== HTTP Request End ===");
AVClient.PrintLog(sb.ToString()); AVClient.PrintLog(sb.ToString());
} }

View File

@ -29,8 +29,8 @@ namespace LeanCloud.Storage.Internal {
public HttpClient() { public HttpClient() {
client = new NetHttpClient(); client = new NetHttpClient();
// TODO 设置版本号 // 设置版本号
client.DefaultRequestHeaders.Add("User-Agent", "LeanCloud-dotNet-SDK/" + "2.0.0"); client.DefaultRequestHeaders.Add("User-Agent", $"LeanCloud-csharp-sdk-{AVClient.Version}");
} }
public HttpClient(NetHttpClient client) { public HttpClient(NetHttpClient client) {

View File

@ -11,14 +11,12 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace LeanCloud namespace LeanCloud {
{
/// <summary> /// <summary>
/// AVClient contains static functions that handle global /// AVClient contains static functions that handle global
/// configuration for the LeanCloud library. /// configuration for the LeanCloud library.
/// </summary> /// </summary>
public static class AVClient public static class AVClient {
{
public static readonly string[] DateFormatStrings = { public static readonly string[] DateFormatStrings = {
// Official ISO format // Official ISO format
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'",
@ -31,13 +29,11 @@ namespace LeanCloud
/// <summary> /// <summary>
/// Represents the configuration of the LeanCloud SDK. /// Represents the configuration of the LeanCloud SDK.
/// </summary> /// </summary>
public struct Configuration public struct Configuration {
{
/// <summary> /// <summary>
/// 与 SDK 通讯的云端节点 /// 与 SDK 通讯的云端节点
/// </summary> /// </summary>
public enum AVRegion public enum AVRegion {
{
/// <summary> /// <summary>
/// 默认值LeanCloud 华北节点,同 Public_North_China /// 默认值LeanCloud 华北节点,同 Public_North_China
/// </summary> /// </summary>
@ -81,8 +77,7 @@ namespace LeanCloud
/// Any values set here will overwrite those that are automatically configured by /// Any values set here will overwrite those that are automatically configured by
/// any platform-specific migration library your app includes. /// any platform-specific migration library your app includes.
/// </summary> /// </summary>
public struct VersionInformation public struct VersionInformation {
{
/// <summary> /// <summary>
/// The build number of your app. /// The build number of your app.
/// </summary> /// </summary>
@ -110,10 +105,8 @@ namespace LeanCloud
/// </summary> /// </summary>
public AVRegion Region { get; set; } public AVRegion Region { get; set; }
internal int RegionValue internal int RegionValue {
{ get {
get
{
return (int)Region; return (int)Region;
} }
} }
@ -178,29 +171,24 @@ namespace LeanCloud
/// </summary> /// </summary>
public static Configuration CurrentConfiguration { get; internal set; } public static Configuration CurrentConfiguration { get; internal set; }
internal static Version Version
{
get
{
var assemblyName = new AssemblyName(typeof(AVClient).GetTypeInfo().Assembly.FullName);
return assemblyName.Version;
}
}
internal static string APIVersion { internal static string APIVersion {
get { get {
return "1.1"; return "1.1";
} }
} }
public static string Name {
get {
return "LeanCloud-CSharp-SDK";
}
}
/// <summary> /// <summary>
/// 当前 SDK 版本号 /// 当前 SDK 版本号
/// </summary> /// </summary>
public static string VersionString public static string Version {
{ get {
get return "0.1.0";
{
return "net-v0.1.0";
} }
} }
@ -214,10 +202,8 @@ namespace LeanCloud
/// </param> /// </param>
/// <param name="applicationKey">The .NET API Key provided in the LeanCloud dashboard. /// <param name="applicationKey">The .NET API Key provided in the LeanCloud dashboard.
/// </param> /// </param>
public static void Initialize(string applicationId, string applicationKey) public static void Initialize(string applicationId, string applicationKey) {
{ Initialize(new Configuration {
Initialize(new Configuration
{
ApplicationId = applicationId, ApplicationId = applicationId,
ApplicationKey = applicationKey ApplicationKey = applicationKey
}); });
@ -229,16 +215,14 @@ namespace LeanCloud
/// 启动日志打印 /// 启动日志打印
/// </summary> /// </summary>
/// <param name="trace"></param> /// <param name="trace"></param>
public static void HttpLog(Action<string> trace) public static void HttpLog(Action<string> trace) {
{
LogTracker = trace; LogTracker = trace;
} }
/// <summary> /// <summary>
/// 打印 HTTP 访问日志 /// 打印 HTTP 访问日志
/// </summary> /// </summary>
/// <param name="log"></param> /// <param name="log"></param>
public static void PrintLog(string log) public static void PrintLog(string log) {
{
LogTracker?.Invoke(log); LogTracker?.Invoke(log);
} }
@ -262,8 +246,7 @@ namespace LeanCloud
/// </summary> /// </summary>
/// <param name="configuration">The configuration to initialize LeanCloud with. /// <param name="configuration">The configuration to initialize LeanCloud with.
/// </param> /// </param>
public static void Initialize(Configuration configuration) public static void Initialize(Configuration configuration) {
{
Config(configuration); Config(configuration);
AVObject.RegisterSubclass<AVUser>(); AVObject.RegisterSubclass<AVUser>();
@ -271,15 +254,11 @@ namespace LeanCloud
AVObject.RegisterSubclass<AVSession>(); AVObject.RegisterSubclass<AVSession>();
} }
internal static void Config(Configuration configuration) internal static void Config(Configuration configuration) {
{ lock (mutex) {
lock (mutex)
{
var nodeHash = configuration.ApplicationId.Split('-'); var nodeHash = configuration.ApplicationId.Split('-');
if (nodeHash.Length > 1) if (nodeHash.Length > 1) {
{ if (nodeHash[1].Trim() == "9Nh9j0Va") {
if (nodeHash[1].Trim() == "9Nh9j0Va")
{
configuration.Region = Configuration.AVRegion.Public_East_China; configuration.Region = Configuration.AVRegion.Public_East_China;
} }
} }
@ -288,8 +267,7 @@ namespace LeanCloud
} }
} }
internal static void Clear() internal static void Clear() {
{
AVPlugins.Instance.AppRouterController.Clear(); AVPlugins.Instance.AppRouterController.Clear();
AVPlugins.Instance.Reset(); AVPlugins.Instance.Reset();
AVUser.ClearInMemoryUser(); AVUser.ClearInMemoryUser();
@ -299,16 +277,13 @@ namespace LeanCloud
/// Switch app. /// Switch app.
/// </summary> /// </summary>
/// <param name="configuration">Configuration.</param> /// <param name="configuration">Configuration.</param>
public static void Switch(Configuration configuration) public static void Switch(Configuration configuration) {
{
Clear(); Clear();
Initialize(configuration); Initialize(configuration);
} }
public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China) public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China) {
{ var configuration = new Configuration {
var configuration = new Configuration
{
ApplicationId = applicationId, ApplicationId = applicationId,
ApplicationKey = applicationKey, ApplicationKey = applicationKey,
Region = region Region = region
@ -316,8 +291,7 @@ namespace LeanCloud
Switch(configuration); Switch(configuration);
} }
public static string BuildQueryString(IDictionary<string, object> parameters) public static string BuildQueryString(IDictionary<string, object> parameters) {
{
return string.Join("&", (from pair in parameters return string.Join("&", (from pair in parameters
let valueString = pair.Value as string let valueString = pair.Value as string
select string.Format("{0}={1}", select string.Format("{0}={1}",
@ -327,11 +301,9 @@ namespace LeanCloud
.ToArray()); .ToArray());
} }
internal static IDictionary<string, string> DecodeQueryString(string queryString) internal static IDictionary<string, string> DecodeQueryString(string queryString) {
{
var dict = new Dictionary<string, string>(); var dict = new Dictionary<string, string>();
foreach (var pair in queryString.Split('&')) foreach (var pair in queryString.Split('&')) {
{
var parts = pair.Split(new char[] { '=' }, 2); var parts = pair.Split(new char[] { '=' }, 2);
dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null; dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null;
} }