chore: 重构了 AVCommand 及 http 模块

oneRain 2019-08-01 17:36:34 +08:00
parent c6bbf3219f
commit ca3b19311c
21 changed files with 494 additions and 674 deletions

View File

@ -10,6 +10,7 @@ namespace LeanCloudTests {
AVClient.Initialize(new AVClient.Configuration {
ApplicationId = "BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz",
ApplicationKey = "pbf6Nk5seyjilexdpyrPwjSp",
ApiServer = "https://avoscloud.com"
});
AVClient.HttpLog(TestContext.Out.WriteLine);
}

View File

@ -0,0 +1,26 @@
using NUnit.Framework;
using System;
using System.Threading.Tasks;
using LeanCloud;
namespace LeanCloudTests {
public class QueryTest {
[SetUp]
public void SetUp() {
AVClient.Initialize(new AVClient.Configuration {
ApplicationId = "BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz",
ApplicationKey = "pbf6Nk5seyjilexdpyrPwjSp",
RTMServer = "https://router-g0-push.avoscloud.com",
});
AVClient.HttpLog(TestContext.Out.WriteLine);
}
[Test]
public async Task TestQuery() {
var query = new AVQuery<AVObject>("Foo");
query.WhereEqualTo("content", "hello, world");
var count = await query.CountAsync();
Assert.Greater(count, 8);
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Utilities;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud.Storage.Internal
{
@ -21,11 +21,11 @@ namespace LeanCloud.Storage.Internal
string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("functions/{0}", Uri.EscapeUriString(name)),
method: "POST",
sessionToken: sessionToken,
data: NoObjectsEncoder.Instance.Encode(parameters) as IDictionary<string, object>);
var command = new EngineCommand {
Path = $"functions/{Uri.EscapeUriString(name)}",
Method = HttpMethod.Post,
Content = parameters
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
@ -39,11 +39,11 @@ namespace LeanCloud.Storage.Internal
public Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters, string sessionToken, CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("call/{0}", Uri.EscapeUriString(name)),
method: "POST",
sessionToken: sessionToken,
data: PointerOrLocalIdEncoder.Instance.Encode(parameters) as IDictionary<string, object>);
var command = new EngineCommand {
Path = $"call/{Uri.EscapeUriString(name)}",
Method = HttpMethod.Post,
Content = parameters
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;

View File

@ -12,158 +12,30 @@ namespace LeanCloud.Storage.Internal
/// AVCommand is an <see cref="HttpRequest"/> with pre-populated
/// headers.
/// </summary>
public class AVCommand : HttpRequest
{
public object Body {
public class AVCommand {
// 不同服务对应的服务器地址不同
public virtual string Server => AVClient.CurrentConfiguration.ApiServer;
public string Path {
get; set;
}
public override Stream Data {
public HttpMethod Method {
get; set;
}
public Dictionary<string, string> Headers {
get; set;
}
public object Content {
get; set;
}
public Uri Uri {
get {
return new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(Body)));
return new Uri($"{Server}/{AVClient.APIVersion}/{Path}");
}
}
public AVCommand(string relativeUri,
string method,
string sessionToken = null,
IList<KeyValuePair<string, string>> headers = null,
object data = null)
{
var state = AVPlugins.Instance.AppRouterController.Get();
var urlTemplate = "https://{0}/{1}/{2}";
AVClient.Configuration configuration = AVClient.CurrentConfiguration;
var apiVersion = "1.1";
if (relativeUri.StartsWith("push", StringComparison.Ordinal) ||
relativeUri.StartsWith("installations", StringComparison.Ordinal)) {
Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri));
if (configuration.PushServer != null) {
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri));
}
} else if (relativeUri.StartsWith("stats", StringComparison.Ordinal) ||
relativeUri.StartsWith("always_collect", StringComparison.Ordinal) ||
relativeUri.StartsWith("statistics", StringComparison.Ordinal)) {
Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri));
if (configuration.StatsServer != null) {
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri));
}
} else if (relativeUri.StartsWith("functions", StringComparison.Ordinal) ||
relativeUri.StartsWith("call", StringComparison.Ordinal)) {
Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri));
if (configuration.EngineServer != null) {
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri));
}
} else {
Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri));
if (configuration.ApiServer != null) {
Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri));
}
}
switch (method) {
case "GET":
Method = HttpMethod.Get;
break;
case "POST":
Method = HttpMethod.Post;
break;
case "DELETE":
Method = HttpMethod.Delete;
break;
case "PUT":
Method = HttpMethod.Put;
break;
case "HEAD":
Method = HttpMethod.Head;
break;
case "TRACE":
Method = HttpMethod.Trace;
break;
default:
break;
}
Body = data;
Headers = new List<KeyValuePair<string, string>>(headers ?? Enumerable.Empty<KeyValuePair<string, string>>());
string useProduction = AVClient.UseProduction ? "1" : "0";
Headers.Add(new KeyValuePair<string, string>("X-LC-Prod", useProduction));
if (!string.IsNullOrEmpty(sessionToken)) {
Headers.Add(new KeyValuePair<string, string>("X-LC-Session", sessionToken));
}
Headers.Add(new KeyValuePair<string, string>("Content-Type", "application/json"));
}
//public AVCommand(string relativeUri,
// string method,
// string sessionToken = null,
// IList<KeyValuePair<string, string>> headers = null,
// Stream stream = null,
// string contentType = null)
//{
// var state = AVPlugins.Instance.AppRouterController.Get();
// var urlTemplate = "https://{0}/{1}/{2}";
// AVClient.Configuration configuration = AVClient.CurrentConfiguration;
// var apiVersion = "1.1";
// if (relativeUri.StartsWith("push") || relativeUri.StartsWith("installations"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri));
// if (configuration.PushServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri));
// }
// }
// else if (relativeUri.StartsWith("stats") || relativeUri.StartsWith("always_collect") || relativeUri.StartsWith("statistics"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri));
// if (configuration.StatsServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri));
// }
// }
// else if (relativeUri.StartsWith("functions") || relativeUri.StartsWith("call"))
// {
// Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri));
// if (configuration.EngineServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri));
// }
// }
// else
// {
// Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri));
// if (configuration.ApiServer != null)
// {
// Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri));
// }
// }
// Method = method;
// Data = stream;
// Headers = new List<KeyValuePair<string, string>>(headers ?? Enumerable.Empty<KeyValuePair<string, string>>());
// string useProduction = AVClient.UseProduction ? "1" : "0";
// Headers.Add(new KeyValuePair<string, string>("X-LC-Prod", useProduction));
// if (!string.IsNullOrEmpty(sessionToken))
// {
// Headers.Add(new KeyValuePair<string, string>("X-LC-Session", sessionToken));
// }
// if (!string.IsNullOrEmpty(contentType))
// {
// Headers.Add(new KeyValuePair<string, string>("Content-Type", contentType));
// }
//}
public AVCommand(AVCommand other)
{
this.Uri = other.Uri;
this.Method = other.Method;
this.Headers = new List<KeyValuePair<string, string>>(other.Headers);
this.Body = other.Data;
}
}
}

View File

@ -1,19 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
using System.Text;
using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal
{
/// <summary>
/// Command Runner.
/// </summary>
public class AVCommandRunner : IAVCommandRunner
{
private readonly IHttpClient httpClient;
public class AVCommandRunner : IAVCommandRunner {
public const string APPLICATION_JSON = "application/json";
private readonly System.Net.Http.HttpClient httpClient;
private readonly IInstallationIdController installationIdController;
/// <summary>
@ -23,7 +26,7 @@ namespace LeanCloud.Storage.Internal
/// <param name="installationIdController"></param>
public AVCommandRunner(IHttpClient httpClient, IInstallationIdController installationIdController)
{
this.httpClient = httpClient;
this.httpClient = new System.Net.Http.HttpClient();
this.installationIdController = installationIdController;
}
@ -35,133 +38,113 @@ namespace LeanCloud.Storage.Internal
/// <param name="downloadProgress"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(HttpRequest command,
public async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command,
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default(CancellationToken))
{
return PrepareCommand(command).ContinueWith(commandTask =>
{
var requestLog = commandTask.Result.ToLog();
AVClient.PrintLog("http=>" + requestLog);
CancellationToken cancellationToken = default(CancellationToken)) {
var request = new HttpRequestMessage {
RequestUri = command.Uri,
Method = command.Method,
Content = new StringContent(Json.Encode(command.Content))
};
return httpClient.ExecuteAsync(commandTask.Result, uploadProgress, downloadProgress, cancellationToken).OnSuccess(t =>
{
cancellationToken.ThrowIfCancellationRequested();
var headers = await GetHeadersAsync();
foreach (var header in headers) {
if (!string.IsNullOrEmpty(header.Value)) {
request.Headers.Add(header.Key, header.Value);
}
}
request.Content.Headers.ContentType = new MediaTypeHeaderValue(APPLICATION_JSON);
var response = t.Result;
var contentString = response.Item2;
int responseCode = (int)response.Item1;
PrintRequest(command, headers);
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
request.Dispose();
var responseLog = responseCode + ";" + contentString;
AVClient.PrintLog("http<=" + responseLog);
var resultString = await response.Content.ReadAsStringAsync();
response.Dispose();
PrintResponse(response, resultString);
if (responseCode >= 500)
{
// Server error, return InternalServerError.
throw new AVException(AVException.ErrorCode.InternalServerError, response.Item2);
var ret = new Tuple<HttpStatusCode, string>(response.StatusCode, resultString);
var responseCode = ret.Item1;
var contentString = ret.Item2;
if (responseCode >= HttpStatusCode.InternalServerError) {
// Server error, return InternalServerError.
throw new AVException(AVException.ErrorCode.InternalServerError, contentString);
}
if (contentString != null) {
IDictionary<string, object> contentJson = null;
try {
if (contentString.StartsWith("[", StringComparison.Ordinal)) {
var arrayJson = Json.Parse(contentString);
contentJson = new Dictionary<string, object> { { "results", arrayJson } };
} else {
contentJson = Json.Parse(contentString) as IDictionary<string, object>;
}
else if (contentString != null)
{
IDictionary<string, object> contentJson = null;
try
{
if (contentString.StartsWith("["))
{
var arrayJson = Json.Parse(contentString);
contentJson = new Dictionary<string, object> { { "results", arrayJson } };
}
else
{
contentJson = Json.Parse(contentString) as IDictionary<string, object>;
}
}
catch (Exception e)
{
throw new AVException(AVException.ErrorCode.OtherCause,
"Invalid response from server", e);
}
if (responseCode < 200 || responseCode > 299)
{
AVClient.PrintLog("error response code:" + responseCode);
int code = (int)(contentJson.ContainsKey("code") ? (int)contentJson["code"] : (int)AVException.ErrorCode.OtherCause);
string error = contentJson.ContainsKey("error") ?
contentJson["error"] as string : contentString;
AVException.ErrorCode ec = (AVException.ErrorCode)code;
throw new AVException(ec, error);
}
return new Tuple<HttpStatusCode, IDictionary<string, object>>(response.Item1,
contentJson);
}
return new Tuple<HttpStatusCode, IDictionary<string, object>>(response.Item1, null);
});
}).Unwrap();
} catch (Exception e) {
throw new AVException(AVException.ErrorCode.OtherCause,
"Invalid response from server", e);
}
if (responseCode < HttpStatusCode.OK || responseCode > HttpStatusCode.PartialContent) {
AVClient.PrintLog("error response code:" + responseCode);
int code = (int)(contentJson.ContainsKey("code") ? (int)contentJson["code"] : (int)AVException.ErrorCode.OtherCause);
string error = contentJson.ContainsKey("error") ?
contentJson["error"] as string : contentString;
AVException.ErrorCode ec = (AVException.ErrorCode)code;
throw new AVException(ec, error);
}
return new Tuple<HttpStatusCode, IDictionary<string, object>>(responseCode, contentJson);
}
return new Tuple<HttpStatusCode, IDictionary<string, object>>(responseCode, null);
}
private const string revocableSessionTokenTrueValue = "1";
private Task<HttpRequest> PrepareCommand(HttpRequest command)
{
HttpRequest newCommand = command;
Task<HttpRequest> installationIdTask = installationIdController.GetAsync().ContinueWith(t =>
{
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Installation-Id", t.Result.ToString()));
return newCommand;
});
AVClient.Configuration configuration = AVClient.CurrentConfiguration;
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Id", configuration.ApplicationId));
async Task<Dictionary<string, string>> GetHeadersAsync() {
var headers = new Dictionary<string, string>();
var installationId = await installationIdController.GetAsync();
headers.Add("X-LC-Installation-Id", installationId.ToString());
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(configuration.MasterKey) && AVClient.UseMasterKey)
{
string sign = MD5.GetMd5String(timestamp + configuration.MasterKey);
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Sign", string.Format("{0},{1},master", sign, timestamp)));
}
else
{
string sign = MD5.GetMd5String(timestamp + configuration.ApplicationKey);
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Sign", string.Format("{0},{1}", sign, timestamp)));
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;
}
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-Client-Version", AVClient.VersionString));
static void PrintRequest(AVCommand request, Dictionary<string, string> headers) {
StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Request Start ===");
sb.AppendLine($"URL: {request.Uri}");
sb.AppendLine($"Method: {request.Method}");
sb.AppendLine($"Headers: {JsonConvert.SerializeObject(headers)}");
sb.AppendLine($"Content: {JsonConvert.SerializeObject(request.Content)}");
sb.AppendLine("=== HTTP Request End ===");
AVClient.PrintLog(sb.ToString());
}
if (!string.IsNullOrEmpty(configuration.VersionInfo.BuildVersion))
{
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-App-Build-Version", configuration.VersionInfo.BuildVersion));
}
if (!string.IsNullOrEmpty(configuration.VersionInfo.DisplayVersion))
{
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-App-Display-Version", configuration.VersionInfo.DisplayVersion));
}
if (!string.IsNullOrEmpty(configuration.VersionInfo.OSVersion))
{
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LC-OS-Version", configuration.VersionInfo.OSVersion));
}
if (AVUser.IsRevocableSessionEnabled)
{
newCommand.Headers.Add(new KeyValuePair<string, string>("X-LeanCloud-Revocable-Session", revocableSessionTokenTrueValue));
}
if (configuration.AdditionalHTTPHeaders != null)
{
var headersDictionary = newCommand.Headers.ToDictionary(kv => kv.Key, kv => kv.Value);
foreach (var header in configuration.AdditionalHTTPHeaders)
{
if (headersDictionary.ContainsKey(header.Key))
{
headersDictionary[header.Key] = header.Value;
}
else
{
newCommand.Headers.Add(header);
}
}
newCommand.Headers = headersDictionary.ToList();
}
return installationIdTask;
static void PrintResponse(HttpResponseMessage response, string content) {
StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Response Start ===");
sb.AppendLine($"URL: {response.RequestMessage.RequestUri}");
sb.AppendLine($"Content: {content}");
sb.AppendLine("=== HTTP Response End ===");
AVClient.PrintLog(sb.ToString());
}
}
}

View File

@ -0,0 +1,7 @@
using System;
namespace LeanCloud.Storage.Internal {
public class EngineCommand : AVCommand {
public override string Server => AVClient.CurrentConfiguration.EngineServer;
}
}

View File

@ -16,7 +16,7 @@ namespace LeanCloud.Storage.Internal
/// <param name="downloadProgress">Download progress callback.</param>
/// <param name="cancellationToken">The cancellation token for the request.</param>
/// <returns></returns>
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(HttpRequest command,
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command,
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
CancellationToken cancellationToken = default(CancellationToken));

View File

@ -0,0 +1,7 @@
using LeanCloud;
namespace LeanCloud.Storage.Internal {
public class RTMCommand : AVCommand {
public override string Server => AVClient.CurrentConfiguration.RTMServer;
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
using System.Threading;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud.Storage.Internal {
/// <summary>
@ -22,10 +22,10 @@ namespace LeanCloud.Storage.Internal {
public IAVCurrentConfigController CurrentConfigController { get; internal set; }
public Task<AVConfig> FetchConfigAsync(String sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand("config",
method: "GET",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = "config",
Method = HttpMethod.Post,
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(task => {
cancellationToken.ThrowIfCancellationRequested();

View File

@ -15,7 +15,7 @@ namespace LeanCloud.Storage.Internal
/// </summary>
public class AVFileController : IAVFileController
{
private readonly IAVCommandRunner commandRunner;
protected readonly IAVCommandRunner commandRunner;
/// <summary>
/// Initializes a new instance of the <see cref="T:LeanCloud.Storage.Internal.AVFileController"/> class.
/// </summary>
@ -66,14 +66,13 @@ namespace LeanCloud.Storage.Internal
}
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken)
{
var command = new AVCommand("files/" + state.ObjectId,
method: "DELETE",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = $"files/{state.ObjectId}",
Method = HttpMethod.Delete
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
internal static Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken)
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken)
{
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
string currentSessionToken = AVUser.CurrentSessionToken;
@ -85,17 +84,19 @@ namespace LeanCloud.Storage.Internal
parameters.Add("mime_type", AVFile.GetMIMEType(str));
parameters.Add("metaData", fileState.MetaData);
rtn = AVClient.RequestAsync("POST", new Uri("fileTokens", UriKind.Relative), currentSessionToken, parameters, cancellationToken);
return rtn;
var command = new AVCommand {
Path = "fileTokens",
Method = HttpMethod.Post,
Content = parameters
};
return commandRunner.RunCommandAsync(command);
}
public Task<FileState> GetAsync(string objectId, string sessionToken, CancellationToken cancellationToken)
{
var command = new AVCommand("files/" + objectId,
method: "GET",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = $"files/{objectId}",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(_ =>
{
var result = _.Result;

View File

@ -161,9 +161,12 @@ namespace LeanCloud.Storage.Internal
parameters.Add("metaData", state.MetaData);
rtn = AVClient.RequestAsync("POST", new Uri("qiniu", UriKind.Relative), currentSessionToken, parameters, cancellationToken);
return rtn;
var command = new AVCommand {
Path = "qiniu",
Method = HttpMethod.Post,
Content = parameters
};
return commandRunner.RunCommandAsync(command);
}
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state)
{

View File

@ -4,32 +4,24 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Utilities;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud.Storage.Internal
{
public class AVObjectController : IAVObjectController
{
namespace LeanCloud.Storage.Internal {
public class AVObjectController : IAVObjectController {
private readonly IAVCommandRunner commandRunner;
public AVObjectController(IAVCommandRunner commandRunner)
{
public AVObjectController(IAVCommandRunner commandRunner) {
this.commandRunner = commandRunner;
}
public Task<IObjectState> FetchAsync(IObjectState state,
string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("classes/{0}/{1}",
Uri.EscapeDataString(state.ClassName),
Uri.EscapeDataString(state.ObjectId)),
method: "GET",
sessionToken: sessionToken,
data: null);
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
CancellationToken cancellationToken) {
var command = new AVCommand {
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});
}
@ -37,18 +29,12 @@ namespace LeanCloud.Storage.Internal
public Task<IObjectState> FetchAsync(IObjectState state,
IDictionary<string, object> queryString,
string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("classes/{0}/{1}?{2}",
Uri.EscapeDataString(state.ClassName),
Uri.EscapeDataString(state.ObjectId),
AVClient.BuildQueryString(queryString)),
method: "GET",
sessionToken: sessionToken,
data: null);
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
CancellationToken cancellationToken) {
var command = new AVCommand {
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});
}
@ -56,22 +42,16 @@ namespace LeanCloud.Storage.Internal
public Task<IObjectState> SaveAsync(IObjectState state,
IDictionary<string, IAVFieldOperation> operations,
string sessionToken,
CancellationToken cancellationToken)
{
CancellationToken cancellationToken) {
var objectJSON = AVObject.ToJSONObjectForSaving(operations);
var command = new AVCommand((state.ObjectId == null ?
string.Format("classes/{0}", Uri.EscapeDataString(state.ClassName)) :
string.Format("classes/{0}/{1}", Uri.EscapeDataString(state.ClassName), state.ObjectId)),
method: (state.ObjectId == null ? "POST" : "PUT"),
sessionToken: sessionToken,
data: objectJSON);
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var command = new AVCommand {
Path = state.ObjectId == null ? $"classes/{Uri.EscapeDataString(state.ClassName)}" : $"classes/{Uri.EscapeDataString(state.ClassName)}/{state.ObjectId}",
Method = state.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
Content = objectJSON
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => {
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
serverState = serverState.MutatedClone(mutableClone =>
{
serverState = serverState.MutatedClone(mutableClone => {
mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created;
});
return serverState;
@ -81,24 +61,20 @@ namespace LeanCloud.Storage.Internal
public IList<Task<IObjectState>> SaveAllAsync(IList<IObjectState> states,
IList<IDictionary<string, IAVFieldOperation>> operationsList,
string sessionToken,
CancellationToken cancellationToken)
{
CancellationToken cancellationToken) {
var requests = states
.Zip(operationsList, (item, ops) => new AVCommand(
item.ObjectId == null
? string.Format("classes/{0}", Uri.EscapeDataString(item.ClassName))
: string.Format("classes/{0}/{1}", Uri.EscapeDataString(item.ClassName), Uri.EscapeDataString(item.ObjectId)),
method: item.ObjectId == null ? "POST" : "PUT",
data: AVObject.ToJSONObjectForSaving(ops)))
.Zip(operationsList, (item, ops) => new AVCommand {
Path = item.ObjectId == null ? $"classes/{Uri.EscapeDataString(item.ClassName)}" : $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}",
Method = item.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
Content = AVObject.ToJSONObjectForSaving(ops)
})
.ToList();
var batchTasks = ExecuteBatchRequests(requests, sessionToken, cancellationToken);
var stateTasks = new List<Task<IObjectState>>();
foreach (var task in batchTasks)
{
stateTasks.Add(task.OnSuccess(t =>
{
foreach (var task in batchTasks) {
stateTasks.Add(task.OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result, AVDecoder.Instance);
}));
}
@ -108,27 +84,23 @@ namespace LeanCloud.Storage.Internal
public Task DeleteAsync(IObjectState state,
string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("classes/{0}/{1}",
state.ClassName, state.ObjectId),
method: "DELETE",
sessionToken: sessionToken,
data: null);
CancellationToken cancellationToken) {
var command = new AVCommand {
Path = $"classes/{state.ClassName}/{state.ObjectId}",
Method = HttpMethod.Delete
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
public IList<Task> DeleteAllAsync(IList<IObjectState> states,
string sessionToken,
CancellationToken cancellationToken)
{
CancellationToken cancellationToken) {
var requests = states
.Where(item => item.ObjectId != null)
.Select(item => new AVCommand(
string.Format("classes/{0}/{1}", Uri.EscapeDataString(item.ClassName), Uri.EscapeDataString(item.ObjectId)),
method: "DELETE",
data: null))
.Select(item => new AVCommand {
Path = $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}",
Method = HttpMethod.Delete
})
.ToList();
return ExecuteBatchRequests(requests, sessionToken, cancellationToken).Cast<Task>().ToList();
}
@ -137,14 +109,12 @@ namespace LeanCloud.Storage.Internal
private const int MaximumBatchSize = 50;
internal IList<Task<IDictionary<string, object>>> ExecuteBatchRequests(IList<AVCommand> requests,
string sessionToken,
CancellationToken cancellationToken)
{
CancellationToken cancellationToken) {
var tasks = new List<Task<IDictionary<string, object>>>();
int batchSize = requests.Count;
IEnumerable<AVCommand> remaining = requests;
while (batchSize > MaximumBatchSize)
{
while (batchSize > MaximumBatchSize) {
var process = remaining.Take(MaximumBatchSize).ToList();
remaining = remaining.Skip(MaximumBatchSize);
@ -159,48 +129,40 @@ namespace LeanCloud.Storage.Internal
private IList<Task<IDictionary<string, object>>> ExecuteBatchRequest(IList<AVCommand> requests,
string sessionToken,
CancellationToken cancellationToken)
{
CancellationToken cancellationToken) {
var tasks = new List<Task<IDictionary<string, object>>>();
int batchSize = requests.Count;
var tcss = new List<TaskCompletionSource<IDictionary<string, object>>>();
for (int i = 0; i < batchSize; ++i)
{
for (int i = 0; i < batchSize; ++i) {
var tcs = new TaskCompletionSource<IDictionary<string, object>>();
tcss.Add(tcs);
tasks.Add(tcs.Task);
}
var encodedRequests = requests.Select(r =>
{
var encodedRequests = requests.Select(r => {
var results = new Dictionary<string, object> {
{ "method", r.Method },
{ "path", r.Uri.AbsolutePath },
{ "path", r.Path },
};
if (r.Body != null)
{
results["body"] = r.Body;
if (r.Content != null) {
results["body"] = r.Content;
}
return results;
}).Cast<object>().ToList();
var command = new AVCommand("batch",
method: "POST",
sessionToken: sessionToken,
data: new Dictionary<string, object> { { "requests", encodedRequests } });
commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
foreach (var tcs in tcss)
{
if (t.IsFaulted)
{
var command = new AVCommand {
Path = "batch",
Method = HttpMethod.Post,
Content = new Dictionary<string, object> {
{ "requests", encodedRequests }
}
};
commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => {
if (t.IsFaulted || t.IsCanceled) {
foreach (var tcs in tcss) {
if (t.IsFaulted) {
tcs.TrySetException(t.Exception);
}
else if (t.IsCanceled)
{
} else if (t.IsCanceled) {
tcs.TrySetCanceled();
}
}
@ -209,33 +171,25 @@ namespace LeanCloud.Storage.Internal
var resultsArray = Conversion.As<IList<object>>(t.Result.Item2["results"]);
int resultLength = resultsArray.Count;
if (resultLength != batchSize)
{
foreach (var tcs in tcss)
{
if (resultLength != batchSize) {
foreach (var tcs in tcss) {
tcs.TrySetException(new InvalidOperationException(
"Batch command result count expected: " + batchSize + " but was: " + resultLength + "."));
}
return;
}
for (int i = 0; i < batchSize; ++i)
{
for (int i = 0; i < batchSize; ++i) {
var result = resultsArray[i] as Dictionary<string, object>;
var tcs = tcss[i];
if (result.ContainsKey("success"))
{
if (result.ContainsKey("success")) {
tcs.TrySetResult(result["success"] as IDictionary<string, object>);
}
else if (result.ContainsKey("error"))
{
} else if (result.ContainsKey("error")) {
var error = result["error"] as IDictionary<string, object>;
long errorCode = long.Parse(error["code"].ToString());
tcs.TrySetException(new AVException((AVException.ErrorCode)errorCode, error["error"] as string));
}
else
{
} else {
tcs.TrySetException(new InvalidOperationException(
"Invalid batch command response."));
}

View File

@ -1,10 +1,9 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
namespace LeanCloud.Storage.Internal
{
@ -75,14 +74,10 @@ namespace LeanCloud.Storage.Internal
string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("{0}?{1}",
relativeUri,
AVClient.BuildQueryString(parameters)),
method: "GET",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = $"{relativeUri}?{AVClient.BuildQueryString(parameters)}",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
return t.Result.Item2;

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud.Storage.Internal {
public class AVSessionController : IAVSessionController {
@ -13,31 +13,28 @@ namespace LeanCloud.Storage.Internal {
}
public Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand("sessions/me",
method: "GET",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = "sessions/me",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});
}
public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand("logout",
method: "POST",
sessionToken: sessionToken,
data: new Dictionary<string, object>());
var command = new AVCommand {
Path = "logout",
Method = HttpMethod.Post
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
public Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
var command = new AVCommand("upgradeToRevocableSession",
method: "POST",
sessionToken: sessionToken,
data: new Dictionary<string, object>());
var command = new AVCommand {
Path = "upgradeToRevocableSession",
Method = HttpMethod.Post,
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => {
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
});

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud.Storage.Internal
{
@ -20,11 +20,11 @@ namespace LeanCloud.Storage.Internal
CancellationToken cancellationToken)
{
var objectJSON = AVObject.ToJSONObjectForSaving(operations);
var command = new AVCommand("classes/_User",
method: "POST",
data: objectJSON);
var command = new AVCommand {
Path = "classes/_User",
Method = HttpMethod.Post,
Content = objectJSON
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
@ -49,11 +49,11 @@ namespace LeanCloud.Storage.Internal
if (email != null) {
data.Add("email", email);
}
var command = new AVCommand("login",
method: "POST",
data: data);
var command = new AVCommand {
Path = "login",
Method = HttpMethod.Post,
Content = data
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
@ -73,12 +73,13 @@ namespace LeanCloud.Storage.Internal
var authData = new Dictionary<string, object>();
authData[authType] = data;
var path = failOnNotExist ? "users?failOnNotExist=true" : "users";
var command = new AVCommand(path,
method: "POST",
data: new Dictionary<string, object> {
var command = new AVCommand {
Path = path,
Method = HttpMethod.Post,
Content = new Dictionary<string, object> {
{ "authData", authData}
});
}
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
@ -92,11 +93,10 @@ namespace LeanCloud.Storage.Internal
public Task<IObjectState> GetUserAsync(string sessionToken, CancellationToken cancellationToken)
{
var command = new AVCommand("users/me",
method: "GET",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = "users/me",
Method = HttpMethod.Get
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
@ -105,22 +105,24 @@ namespace LeanCloud.Storage.Internal
public Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken)
{
var command = new AVCommand("requestPasswordReset",
method: "POST",
data: new Dictionary<string, object> {
var command = new AVCommand {
Path = "requestPasswordReset",
Method = HttpMethod.Post,
Content = new Dictionary<string, object> {
{ "email", email}
});
}
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
public Task<IObjectState> LogInWithParametersAsync(string relativeUrl, IDictionary<string, object> data,
CancellationToken cancellationToken)
{
var command = new AVCommand(string.Format("{0}", relativeUrl),
method: "POST",
data: data);
var command = new AVCommand {
Path = relativeUrl,
Method = HttpMethod.Post,
Content = data
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
@ -134,23 +136,24 @@ namespace LeanCloud.Storage.Internal
public Task UpdatePasswordAsync(string userId, string sessionToken, string oldPassword, string newPassword, CancellationToken cancellationToken)
{
var command = new AVCommand(String.Format("users/{0}/updatePassword", userId),
method: "PUT",
sessionToken: sessionToken,
data: new Dictionary<string, object> {
{"old_password", oldPassword},
{"new_password", newPassword},
});
var command = new AVCommand {
Path = $"users/{userId}/updatePassword",
Method = HttpMethod.Put,
Content = new Dictionary<string, object> {
{ "old_password", oldPassword },
{ "new_password", newPassword },
}
};
return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
public Task<IObjectState> RefreshSessionTokenAsync(string userId, string sessionToken,
CancellationToken cancellationToken)
{
var command = new AVCommand(String.Format("users/{0}/refreshSessionToken", userId),
method: "PUT",
sessionToken: sessionToken,
data: null);
var command = new AVCommand {
Path = $"users/{userId}/refreshSessionToken",
Method = HttpMethod.Put
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t =>
{
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);

View File

@ -190,6 +190,12 @@ namespace LeanCloud
}
}
internal static string APIVersion {
get {
return "1.1";
}
}
private static readonly string versionString;
/// <summary>
/// 当前 SDK 版本号
@ -431,16 +437,6 @@ namespace LeanCloud
cancellationToken.ThrowIfCancellationRequested();
return new Tuple<HttpStatusCode, IDictionary<string, object>>(code, strs);
}
internal static Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RequestAsync(string method, Uri relativeUri, string sessionToken, IDictionary<string, object> data, CancellationToken cancellationToken)
{
var command = new AVCommand(relativeUri.ToString(),
method: method,
sessionToken: sessionToken,
data: data);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken);
}
internal static Task<Tuple<HttpStatusCode, IDictionary<string, object>>> RunCommandAsync(AVCommand command)
{
@ -452,42 +448,5 @@ namespace LeanCloud
var codeValue = (int)responseStatus;
return (codeValue > 199) && (codeValue < 204);
}
public static string ToLog(this HttpRequest request)
{
StringBuilder sb = new StringBuilder();
var start = "===HTTP Request Start===";
sb.AppendLine(start);
var urlLog = "Url: " + request.Uri;
sb.AppendLine(urlLog);
var methodLog = "Method: " + request.Method;
sb.AppendLine(methodLog);
try
{
var headers = request.Headers.ToDictionary(x => x.Key, x => x.Value as object);
var headersLog = "Headers: " + Json.Encode(headers);
sb.AppendLine(headersLog);
}
catch (Exception)
{
}
try
{
var bodyLog = "Body:" + Json.Encode(request.Data);
sb.AppendLine(bodyLog);
}
catch (Exception)
{
}
var end = "===HTTP Request End===";
sb.AppendLine(end);
return sb.ToString();
}
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud {
/// <summary>
@ -77,37 +78,6 @@ namespace LeanCloud {
}).Unwrap();
}
/// <summary>
/// 获取 LeanCloud 服务器的时间
/// <remarks>
/// 如果获取失败,将返回 DateTime.MinValue
/// </remarks>
/// </summary>
/// <returns>服务器的时间</returns>
public static Task<DateTime> GetServerDateTimeAsync()
{
var command = new AVCommand(relativeUri: "date",
method: "GET",
sessionToken: null,
data: null);
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
DateTime rtn = DateTime.MinValue;
if (AVClient.IsSuccessStatusCode(t.Result.Item1))
{
var date = AVDecoder.Instance.Decode(t.Result.Item2);
if (date != null)
{
if (date is DateTime)
{
rtn = (DateTime)date;
}
}
}
return rtn;
});
}
/// <summary>
/// 请求短信认证。
/// </summary>
@ -154,10 +124,11 @@ namespace LeanCloud {
{
strs.Add("TTL", ttl);
}
var command = new AVCommand("requestSmsCode",
method: "POST",
sessionToken: null,
data: strs);
var command = new EngineCommand {
Path = "requestSmsCode",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -227,10 +198,11 @@ namespace LeanCloud {
{
strs.Add(key, env[key]);
}
var command = new AVCommand("requestSmsCode",
method: "POST",
sessionToken: null,
data: strs);
var command = new EngineCommand {
Path = "requestSmsCode",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -248,17 +220,18 @@ namespace LeanCloud {
{
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
}
Dictionary<string, object> strs = new Dictionary<string, object>()
Dictionary<string, object> body = new Dictionary<string, object>()
{
{ "mobilePhoneNumber", mobilePhoneNumber },
{ "smsType", "voice" },
{ "IDD","+86" }
{ "IDD", "+86" }
};
var command = new AVCommand("requestSmsCode",
method: "POST",
sessionToken: null,
data: strs);
var command = new EngineCommand {
Path = "requestSmsCode",
Method = HttpMethod.Post,
Content = body
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
@ -286,11 +259,9 @@ namespace LeanCloud {
/// <param name="cancellationToken">Cancellation token.</param>
public static Task<bool> VerifySmsCodeAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken)
{
var command = new AVCommand("verifySmsCode/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(),
method: "POST",
sessionToken: null,
data: null);
var command = new AVCommand {
Path = $"verifySmsCode/{code.Trim()}?mobilePhoneNumber={mobilePhoneNumber.Trim()}",
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -334,7 +305,10 @@ namespace LeanCloud {
public static Task<Captcha> RequestCaptchaAsync(int width = 85, int height = 30, CancellationToken cancellationToken = default(CancellationToken))
{
var path = String.Format("requestCaptcha?width={0}&height={1}", width, height);
var command = new AVCommand(path, method: "GET", sessionToken: null, data: null);
var command = new AVCommand {
Path = $"requestCaptcha?width={width}&height={height}",
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
@ -355,12 +329,15 @@ namespace LeanCloud {
/// <returns></returns>
public static Task<string> VerifyCaptchaAsync(string code, string token, CancellationToken cancellationToken = default(CancellationToken))
{
var data = new Dictionary<string, object>
{
var data = new Dictionary<string, object> {
{ "captcha_token", token },
{ "captcha_code", code },
};
var command = new AVCommand("verifyCaptcha", method: "POST", sessionToken: null, data: data);
var command = new AVCommand {
Path = "verifyCaptcha",
Method = HttpMethod.Post,
Content = data
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t =>
{
if (!t.Result.Item2.ContainsKey("validate_token"))
@ -376,11 +353,10 @@ namespace LeanCloud {
/// <returns></returns>
public static Task<IDictionary<string, object>> GetCustomParametersAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var command = new AVCommand(string.Format("statistics/apps/{0}/sendPolicy", AVClient.CurrentConfiguration.ApplicationId),
method: "GET",
sessionToken: null,
data: null);
var command = new AVCommand {
Path = $"statistics/apps/{AVClient.CurrentConfiguration.ApplicationId}/sendPolicy",
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
{
var settings = t.Result.Item2;
@ -407,14 +383,13 @@ namespace LeanCloud {
public static Task<RealtimeSignature> RequestRealtimeSignatureAsync(AVUser user, CancellationToken cancellationToken = default(CancellationToken))
{
var command = new AVCommand(string.Format("rtm/sign"),
method: "POST",
sessionToken: null,
data: new Dictionary<string, object>
{
{ "session_token", user.SessionToken },
}
);
var command = new AVCommand {
Path = "rtm/sign",
Method = HttpMethod.Post,
Content = new Dictionary<string, string> {
{ "session_token", user.SessionToken }
}
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t =>
{
var body = t.Result.Item2;
@ -517,12 +492,11 @@ namespace LeanCloud {
{
var user = t.Result;
var encodedParameters = Encode(parameters);
var command = new AVCommand(
string.Format("call/{0}", Uri.EscapeUriString(this.FunctionName)),
method: "POST",
sessionToken: user != null ? user.SessionToken : null,
data: encodedParameters);
var command = new EngineCommand {
Path = $"call/{Uri.EscapeUriString(FunctionName)}",
Method = HttpMethod.Post,
Content = encodedParameters
};
return AVClient.RunCommandAsync(command);
}).Unwrap().OnSuccess(s =>

View File

@ -2,7 +2,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@ -355,14 +355,18 @@ namespace LeanCloud
};
AVCommand cmd = null;
if (!string.IsNullOrEmpty(this.ObjectId))
{
cmd = new AVCommand("files/" + this.ObjectId,
method: "PUT", sessionToken: AVUser.CurrentSessionToken, data: strs);
}
else
{
cmd = new AVCommand("files", method: "POST", sessionToken: AVUser.CurrentSessionToken, data: strs);
if (!string.IsNullOrEmpty(this.ObjectId)) {
cmd = new AVCommand {
Path = $"files/{ObjectId}",
Method = HttpMethod.Put,
Content = strs
};
} else {
cmd = new AVCommand {
Path = "files",
Method = HttpMethod.Post,
Content = strs
};
}
return AVPlugins.Instance.CommandRunner.RunCommandAsync(cmd).ContinueWith(t =>

View File

@ -1,12 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using LeanCloud.Storage.Internal;
using System.Net.Http;
namespace LeanCloud
{
@ -209,7 +208,7 @@ namespace LeanCloud
}).Unwrap().OnSuccess(t =>
{
IObjectState state = t.Result;
return state == null ? default(T) : AVObject.FromState<T>(state, ClassName);
return state == null ? default : AVObject.FromState<T>(state, ClassName);
});
}
@ -310,11 +309,10 @@ namespace LeanCloud
internal static Task<IEnumerable<T>> rebuildObjectFromCloudQueryResult(string queryString)
{
var command = new AVCommand(queryString,
method: "GET",
sessionToken: AVUser.CurrentSessionToken,
data: null);
var command = new AVCommand {
Path = queryString,
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: CancellationToken.None).OnSuccess(t =>
{
var items = t.Result.Item2["results"] as IList<object>;

View File

@ -1,9 +1,7 @@
using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@ -69,9 +67,10 @@ namespace LeanCloud
return Task.FromResult(false);
}
}
var command = new AVCommand(String.Format("users/me?session_token={0}", CurrentSessionToken),
method: "GET",
data: null);
var command = new AVCommand {
Path = $"users/me?session_token={CurrentSessionToken}",
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -297,10 +296,11 @@ namespace LeanCloud
{
data = this.EncodeForSaving(data);
}
var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId),
method: "POST",
sessionToken: CurrentSessionToken,
data: data);
var command = new AVCommand {
Path = $"users/{ObjectId}/friendship/{userObjectId}",
Method = HttpMethod.Post,
Content = data
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -314,10 +314,10 @@ namespace LeanCloud
/// <returns></returns>
public Task<bool> UnfollowAsync(string userObjectId)
{
var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId),
method: "DELETE",
sessionToken: CurrentSessionToken,
data: null);
var command = new AVCommand {
Path = $"users/{ObjectId}/friendship/{userObjectId}",
Method = HttpMethod.Delete
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1045,10 +1045,11 @@ namespace LeanCloud
{
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestLoginSmsCode",
method: "POST",
sessionToken: CurrentSessionToken,
data: strs);
var command = new AVCommand {
Path = "requestLoginSmsCode",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1183,10 +1184,11 @@ namespace LeanCloud
{
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestPasswordResetBySmsCode",
method: "POST",
sessionToken: currentSessionToken,
data: strs);
var command = new AVCommand {
Path = "requestPasswordResetBySmsCode",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1218,10 +1220,11 @@ namespace LeanCloud
{
{ "password", newPassword }
};
var command = new AVCommand("resetPasswordBySmsCode/" + smsCode,
method: "PUT",
sessionToken: currentSessionToken,
data: strs);
var command = new AVCommand {
Path = $"resetPasswordBySmsCode/{smsCode}",
Method = HttpMethod.Put,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1278,10 +1281,11 @@ namespace LeanCloud
{
strs.Add("validate_token", validateToken);
}
var command = new AVCommand("requestMobilePhoneVerify",
method: "POST",
sessionToken: currentSessionToken,
data: strs);
var command = new AVCommand {
Path = "requestMobilePhoneVerify",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1308,10 +1312,10 @@ namespace LeanCloud
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken)
{
var command = new AVCommand("verifyMobilePhone/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(),
method: "POST",
sessionToken: null,
data: null);
var command = new AVCommand {
Path = $"verifyMobilePhone/{code.Trim()}?mobilePhoneNumber={mobilePhoneNumber.Trim()}",
Method = HttpMethod.Post
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1325,11 +1329,10 @@ namespace LeanCloud
/// <returns></returns>
public static Task<bool> VerifyMobilePhoneAsync(string code)
{
var command = new AVCommand("verifyMobilePhone/" + code.Trim(),
method: "POST",
sessionToken: null,
data: null);
var command = new AVCommand {
Path = $"verifyMobilePhone/{code.Trim()}",
Method = HttpMethod.Post
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);
@ -1363,10 +1366,11 @@ namespace LeanCloud
{
{ "email", email }
};
var command = new AVCommand("requestEmailVerify",
method: "POST",
sessionToken: null,
data: strs);
var command = new AVCommand {
Path = "requestEmailVerify",
Method = HttpMethod.Post,
Content = strs
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t =>
{
return AVClient.IsSuccessStatusCode(t.Result.Item1);

View File

@ -2,8 +2,7 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using LeanCloud.Storage.Internal;
using System.IO;
using System.Text;
using System.Net.Http;
namespace LeanCloud {
/// <summary>
@ -153,7 +152,11 @@ namespace LeanCloud {
{ "versionChangeInterval", versionChangeInterval.ToString().ToLower() },
{ "updateStrategy", updateStrategy.ToString().ToLower() },
};
var command = new AVCommand("leaderboard/leaderboards", "POST", data: data);
var command = new AVCommand {
Path = "leaderboard/leaderboards",
Method = HttpMethod.Post,
Content = data
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
var leaderboard = Parse(t.Result.Item2);
@ -211,7 +214,11 @@ namespace LeanCloud {
if (overwrite) {
path = string.Format("{0}?overwrite=1", path);
}
var command = new AVCommand(path, "POST", user.SessionToken, data: data);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Post,
Content = data
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
List<AVStatistic> statisticList = new List<AVStatistic>();
@ -242,7 +249,10 @@ namespace LeanCloud {
path = string.Format("{0}?statistics={1}", path, names);
}
var sessionToken = AVUser.CurrentUser?.SessionToken;
var command = new AVCommand(path, "GET", sessionToken, data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Post
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
List<AVStatistic> statisticList = new List<AVStatistic>();
@ -272,7 +282,10 @@ namespace LeanCloud {
var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId);
var names = string.Join(",", statisticNames.ToArray());
path = string.Format("{0}?statistics={1}", path, names);
var command = new AVCommand(path, "DELETE", sessionToken: user.SessionToken, data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Delete,
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command);
}
@ -285,7 +298,10 @@ namespace LeanCloud {
public Task<List<AVLeaderboardArchive>> GetArchives(int skip = 0, int limit = 10) {
var path = string.Format("leaderboard/leaderboards/{0}/archives", StatisticName);
path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit);
var command = new AVCommand(path, "GET", data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
List<AVLeaderboardArchive> archives = new List<AVLeaderboardArchive>();
List<object> list = t.Result.Item2["results"] as List<object>;
@ -342,7 +358,10 @@ namespace LeanCloud {
var statistics = string.Join(",", includeStatistics.ToArray());
path = string.Format("{0}&includeStatistics={1}", path, statistics);
}
var command = new AVCommand(path, "GET", data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
List<AVRanking> rankingList = new List<AVRanking>();
@ -389,7 +408,11 @@ namespace LeanCloud {
Task<IDictionary<string,object>> Update(Dictionary<string, object> data) {
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
var command = new AVCommand(path, "PUT", data: data);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Put,
Content = data
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
return t.Result.Item2;
});
@ -401,7 +424,10 @@ namespace LeanCloud {
/// <returns>排行榜对象</returns>
public Task<AVLeaderboard> Fetch() {
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
var command = new AVCommand(path, "GET", data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Get
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
// 反序列化 Leaderboard 对象
@ -419,7 +445,10 @@ namespace LeanCloud {
/// <returns>排行榜对象</returns>
public Task<AVLeaderboard> Reset() {
var path = string.Format("leaderboard/leaderboards/{0}/incrementVersion", StatisticName);
var command = new AVCommand(path, "PUT", data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Put
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => {
try {
Init(t.Result.Item2);
@ -435,7 +464,10 @@ namespace LeanCloud {
/// </summary>
public Task Destroy() {
var path = string.Format("leaderboard/leaderboards/{0}", StatisticName);
var command = new AVCommand(path, "DELETE", data: null);
var command = new AVCommand {
Path = path,
Method = HttpMethod.Delete
};
return AVPlugins.Instance.CommandRunner.RunCommandAsync(command);
}