chore: 规范 http 请求 headers
parent
265a34aa9b
commit
f2ec04a331
|
@ -25,7 +25,14 @@ namespace LeanCloud.Storage.Internal
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Net.Http.Headers;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LeanCloud.Storage.Internal {
|
||||
|
@ -13,9 +14,35 @@ namespace LeanCloud.Storage.Internal {
|
|||
/// Command Runner.
|
||||
/// </summary>
|
||||
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>
|
||||
///
|
||||
|
@ -30,21 +57,22 @@ namespace LeanCloud.Storage.Internal {
|
|||
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
|
||||
CancellationToken cancellationToken = default) {
|
||||
|
||||
string content = JsonConvert.SerializeObject(command.Content);
|
||||
var request = new HttpRequestMessage {
|
||||
RequestUri = command.Uri,
|
||||
Method = command.Method,
|
||||
Content = new StringContent(JsonConvert.SerializeObject(command.Content))
|
||||
Content = new StringContent(content)
|
||||
};
|
||||
|
||||
var headers = GetHeadersAsync();
|
||||
foreach (var header in headers) {
|
||||
if (!string.IsNullOrEmpty(header.Value)) {
|
||||
request.Content.Headers.ContentType = new MediaTypeHeaderValue(APPLICATION_JSON);
|
||||
// 特殊 Headers
|
||||
if (command.Headers != null) {
|
||||
foreach (KeyValuePair<string, string> header in command.Headers) {
|
||||
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);
|
||||
request.Dispose();
|
||||
|
||||
|
@ -87,38 +115,22 @@ namespace LeanCloud.Storage.Internal {
|
|||
return new Tuple<HttpStatusCode, T>(responseCode, default);
|
||||
}
|
||||
|
||||
private const string revocableSessionTokenTrueValue = "1";
|
||||
|
||||
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) {
|
||||
static void PrintRequest(System.Net.Http.HttpClient client, HttpRequestMessage request, string content) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("=== HTTP Request Start ===");
|
||||
sb.AppendLine($"URL: {request.Uri}");
|
||||
sb.AppendLine($"URL: {request.RequestUri}");
|
||||
sb.AppendLine($"Method: {request.Method}");
|
||||
sb.AppendLine($"Headers: {JsonConvert.SerializeObject(headers)}");
|
||||
sb.AppendLine($"Content: {JsonConvert.SerializeObject(request.Content)}");
|
||||
sb.AppendLine($"Headers: ");
|
||||
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 ===");
|
||||
AVClient.PrintLog(sb.ToString());
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ namespace LeanCloud.Storage.Internal {
|
|||
|
||||
public HttpClient() {
|
||||
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) {
|
||||
|
|
|
@ -11,14 +11,12 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LeanCloud
|
||||
{
|
||||
namespace LeanCloud {
|
||||
/// <summary>
|
||||
/// AVClient contains static functions that handle global
|
||||
/// configuration for the LeanCloud library.
|
||||
/// </summary>
|
||||
public static class AVClient
|
||||
{
|
||||
public static class AVClient {
|
||||
public static readonly string[] DateFormatStrings = {
|
||||
// Official ISO format
|
||||
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'",
|
||||
|
@ -31,13 +29,11 @@ namespace LeanCloud
|
|||
/// <summary>
|
||||
/// Represents the configuration of the LeanCloud SDK.
|
||||
/// </summary>
|
||||
public struct Configuration
|
||||
{
|
||||
public struct Configuration {
|
||||
/// <summary>
|
||||
/// 与 SDK 通讯的云端节点
|
||||
/// </summary>
|
||||
public enum AVRegion
|
||||
{
|
||||
public enum AVRegion {
|
||||
/// <summary>
|
||||
/// 默认值,LeanCloud 华北节点,同 Public_North_China
|
||||
/// </summary>
|
||||
|
@ -81,8 +77,7 @@ namespace LeanCloud
|
|||
/// Any values set here will overwrite those that are automatically configured by
|
||||
/// any platform-specific migration library your app includes.
|
||||
/// </summary>
|
||||
public struct VersionInformation
|
||||
{
|
||||
public struct VersionInformation {
|
||||
/// <summary>
|
||||
/// The build number of your app.
|
||||
/// </summary>
|
||||
|
@ -110,10 +105,8 @@ namespace LeanCloud
|
|||
/// </summary>
|
||||
public AVRegion Region { get; set; }
|
||||
|
||||
internal int RegionValue
|
||||
{
|
||||
get
|
||||
{
|
||||
internal int RegionValue {
|
||||
get {
|
||||
return (int)Region;
|
||||
}
|
||||
}
|
||||
|
@ -178,29 +171,24 @@ namespace LeanCloud
|
|||
/// </summary>
|
||||
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 {
|
||||
get {
|
||||
return "1.1";
|
||||
}
|
||||
}
|
||||
|
||||
public static string Name {
|
||||
get {
|
||||
return "LeanCloud-CSharp-SDK";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前 SDK 版本号
|
||||
/// </summary>
|
||||
public static string VersionString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "net-v0.1.0";
|
||||
public static string Version {
|
||||
get {
|
||||
return "0.1.0";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,10 +202,8 @@ namespace LeanCloud
|
|||
/// </param>
|
||||
/// <param name="applicationKey">The .NET API Key provided in the LeanCloud dashboard.
|
||||
/// </param>
|
||||
public static void Initialize(string applicationId, string applicationKey)
|
||||
{
|
||||
Initialize(new Configuration
|
||||
{
|
||||
public static void Initialize(string applicationId, string applicationKey) {
|
||||
Initialize(new Configuration {
|
||||
ApplicationId = applicationId,
|
||||
ApplicationKey = applicationKey
|
||||
});
|
||||
|
@ -229,16 +215,14 @@ namespace LeanCloud
|
|||
/// 启动日志打印
|
||||
/// </summary>
|
||||
/// <param name="trace"></param>
|
||||
public static void HttpLog(Action<string> trace)
|
||||
{
|
||||
public static void HttpLog(Action<string> trace) {
|
||||
LogTracker = trace;
|
||||
}
|
||||
/// <summary>
|
||||
/// 打印 HTTP 访问日志
|
||||
/// </summary>
|
||||
/// <param name="log"></param>
|
||||
public static void PrintLog(string log)
|
||||
{
|
||||
public static void PrintLog(string log) {
|
||||
LogTracker?.Invoke(log);
|
||||
}
|
||||
|
||||
|
@ -262,8 +246,7 @@ namespace LeanCloud
|
|||
/// </summary>
|
||||
/// <param name="configuration">The configuration to initialize LeanCloud with.
|
||||
/// </param>
|
||||
public static void Initialize(Configuration configuration)
|
||||
{
|
||||
public static void Initialize(Configuration configuration) {
|
||||
Config(configuration);
|
||||
|
||||
AVObject.RegisterSubclass<AVUser>();
|
||||
|
@ -271,15 +254,11 @@ namespace LeanCloud
|
|||
AVObject.RegisterSubclass<AVSession>();
|
||||
}
|
||||
|
||||
internal static void Config(Configuration configuration)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
internal static void Config(Configuration configuration) {
|
||||
lock (mutex) {
|
||||
var nodeHash = configuration.ApplicationId.Split('-');
|
||||
if (nodeHash.Length > 1)
|
||||
{
|
||||
if (nodeHash[1].Trim() == "9Nh9j0Va")
|
||||
{
|
||||
if (nodeHash.Length > 1) {
|
||||
if (nodeHash[1].Trim() == "9Nh9j0Va") {
|
||||
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.Reset();
|
||||
AVUser.ClearInMemoryUser();
|
||||
|
@ -299,16 +277,13 @@ namespace LeanCloud
|
|||
/// Switch app.
|
||||
/// </summary>
|
||||
/// <param name="configuration">Configuration.</param>
|
||||
public static void Switch(Configuration configuration)
|
||||
{
|
||||
public static void Switch(Configuration configuration) {
|
||||
Clear();
|
||||
Initialize(configuration);
|
||||
}
|
||||
|
||||
public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China)
|
||||
{
|
||||
var configuration = new Configuration
|
||||
{
|
||||
public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China) {
|
||||
var configuration = new Configuration {
|
||||
ApplicationId = applicationId,
|
||||
ApplicationKey = applicationKey,
|
||||
Region = region
|
||||
|
@ -316,8 +291,7 @@ namespace LeanCloud
|
|||
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
|
||||
let valueString = pair.Value as string
|
||||
select string.Format("{0}={1}",
|
||||
|
@ -327,11 +301,9 @@ namespace LeanCloud
|
|||
.ToArray());
|
||||
}
|
||||
|
||||
internal static IDictionary<string, string> DecodeQueryString(string queryString)
|
||||
{
|
||||
internal static IDictionary<string, string> DecodeQueryString(string queryString) {
|
||||
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);
|
||||
dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue