chore: 规范 http 请求 headers
parent
265a34aa9b
commit
f2ec04a331
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue