* UserTest.cs: chore: 整理 Http 模块,跑通所有单元测试

* LCHttpClient.cs:
oneRain 2020-03-03 15:08:59 +08:00
parent 0809a9c558
commit da7a92ea12
2 changed files with 82 additions and 83 deletions

View File

@ -58,6 +58,7 @@ namespace LeanCloud.Test {
[Test] [Test]
public async Task LoginBySessionToken() { public async Task LoginBySessionToken() {
await LCUser.Logout();
string sessionToken = "luo2fpl4qij2050e7enqfz173"; string sessionToken = "luo2fpl4qij2050e7enqfz173";
await LCUser.BecomeWithSessionToken(sessionToken); await LCUser.BecomeWithSessionToken(sessionToken);
LCUser current = await LCUser.GetCurrent(); LCUser current = await LCUser.GetCurrent();

View File

@ -5,23 +5,27 @@ using System.Threading.Tasks;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text;
using System.Security.Cryptography;
using Newtonsoft.Json; using Newtonsoft.Json;
using LeanCloud.Common; using LeanCloud.Common;
namespace LeanCloud.Storage.Internal.Http { namespace LeanCloud.Storage.Internal.Http {
internal class LCHttpClient { internal class LCHttpClient {
string appId; readonly string appId;
string appKey; readonly string appKey;
string server; readonly string server;
string sdkVersion; readonly string sdkVersion;
string apiVersion; readonly string apiVersion;
HttpClient client; HttpClient client;
MD5 md5;
internal LCHttpClient(string appId, string appKey, string server, string sdkVersion, string apiVersion) { internal LCHttpClient(string appId, string appKey, string server, string sdkVersion, string apiVersion) {
this.appId = appId; this.appId = appId;
this.appKey = appKey; this.appKey = appKey;
@ -34,34 +38,20 @@ namespace LeanCloud.Storage.Internal.Http {
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product)); client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-LC-Id", appId); client.DefaultRequestHeaders.Add("X-LC-Id", appId);
// TODO 改为 signature
client.DefaultRequestHeaders.Add("X-LC-Key", appKey); md5 = MD5.Create();
} }
internal async Task<T> Get<T>(string path, internal async Task<T> Get<T>(string path,
Dictionary<string, object> headers = null, Dictionary<string, object> headers = null,
Dictionary<string, object> queryParams = null) { Dictionary<string, object> queryParams = null) {
string url = BuildUrl(path, queryParams);
string url = $"{server}/{apiVersion}/{path}";
if (queryParams != null) {
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
string queries = string.Join("&", queryPairs);
url = $"{url}?{queries}";
}
HttpRequestMessage request = new HttpRequestMessage { HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri(url), RequestUri = new Uri(url),
Method = HttpMethod.Get Method = HttpMethod.Get
}; };
if (headers != null) { await FillHeaders(request.Headers, headers);
foreach (KeyValuePair<string, object> kv in headers) {
request.Headers.Add(kv.Key, kv.Value.ToString());
}
}
LCUser currentUser = await LCUser.GetCurrent();
if (currentUser != null) {
request.Headers.Add("X-LC-Session", currentUser.SessionToken);
}
HttpUtils.PrintRequest(client, request); HttpUtils.PrintRequest(client, request);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
request.Dispose(); request.Dispose();
@ -70,39 +60,30 @@ namespace LeanCloud.Storage.Internal.Http {
response.Dispose(); response.Dispose();
HttpUtils.PrintResponse(response, resultString); HttpUtils.PrintResponse(response, resultString);
HttpStatusCode statusCode = response.StatusCode;
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
return ret; return ret;
} }
int code = (int)statusCode; throw HandleErrorResponse(response.StatusCode, resultString);
string message = resultString;
try {
// 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
code = (int)error["code"];
message = error["error"].ToString();
} catch (Exception e) {
Logger.Error(e.Message);
} finally {
throw new LCException(code, message);
}
} }
internal async Task<T> Post<T>(string path, internal async Task<T> Post<T>(string path,
Dictionary<string, object> headers = null, Dictionary<string, object> headers = null,
Dictionary<string, object> data = null, Dictionary<string, object> data = null,
Dictionary<string, object> queryParams = null) { Dictionary<string, object> queryParams = null) {
string url = BuildUrl(path, queryParams);
HttpRequestMessage request = new HttpRequestMessage { HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri($"{server}/{apiVersion}/{path}"), RequestUri = new Uri(url),
Method = HttpMethod.Post, Method = HttpMethod.Post,
}; };
await FillHeaders(request.Headers, headers);
string content = null; string content = null;
if (data != null) { if (data != null) {
content = JsonConvert.SerializeObject(data); content = JsonConvert.SerializeObject(data);
StringContent requestContent = new StringContent(content); StringContent requestContent = new StringContent(content);
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = requestContent; request.Content = requestContent;
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
} }
HttpUtils.PrintRequest(client, request, content); HttpUtils.PrintRequest(client, request, content);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
@ -112,45 +93,30 @@ namespace LeanCloud.Storage.Internal.Http {
response.Dispose(); response.Dispose();
HttpUtils.PrintResponse(response, resultString); HttpUtils.PrintResponse(response, resultString);
HttpStatusCode statusCode = response.StatusCode;
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
return ret; return ret;
} }
int code = (int)statusCode; throw HandleErrorResponse(response.StatusCode, resultString);
string message = resultString;
try {
// 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
code = (int)error["code"];
message = error["error"].ToString();
} catch (Exception e) {
Logger.Error(e.Message);
} finally {
throw new LCException(code, message);
}
} }
internal async Task<T> Put<T>(string path, internal async Task<T> Put<T>(string path,
Dictionary<string, object> headers = null, Dictionary<string, object> headers = null,
Dictionary<string, object> data = null, Dictionary<string, object> data = null,
Dictionary<string, object> queryParams = null) { Dictionary<string, object> queryParams = null) {
string url = $"{server}/{apiVersion}/{path}"; string url = BuildUrl(path, queryParams);
if (queryParams != null) {
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
string queries = string.Join("&", queryPairs);
url = $"{url}?{queries}";
}
string content = (data != null) ? JsonConvert.SerializeObject(data) : null;
HttpRequestMessage request = new HttpRequestMessage { HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri(url), RequestUri = new Uri(url),
Method = HttpMethod.Put, Method = HttpMethod.Put,
Content = new StringContent(content)
}; };
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); await FillHeaders(request.Headers, headers);
LCUser currentUser = await LCUser.GetCurrent();
if (currentUser != null) { string content = null;
request.Headers.Add("X-LC-Session", currentUser.SessionToken); if (data != null) {
content = JsonConvert.SerializeObject(data);
StringContent requestContent = new StringContent(content);
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = requestContent;
} }
HttpUtils.PrintRequest(client, request, content); HttpUtils.PrintRequest(client, request, content);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
@ -160,30 +126,21 @@ namespace LeanCloud.Storage.Internal.Http {
response.Dispose(); response.Dispose();
HttpUtils.PrintResponse(response, resultString); HttpUtils.PrintResponse(response, resultString);
HttpStatusCode statusCode = response.StatusCode;
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter()); T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
return ret; return ret;
} }
int code = (int)statusCode; throw HandleErrorResponse(response.StatusCode, resultString);
string message = resultString;
try {
// 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
code = (int)error["code"];
message = error["error"].ToString();
} catch (Exception e) {
Logger.Error(e.Message);
} finally {
throw new LCException(code, message);
}
} }
internal async Task Delete(string path) { internal async Task Delete(string path) {
string url = BuildUrl(path);
HttpRequestMessage request = new HttpRequestMessage { HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri($"{server}/{apiVersion}/{path}"), RequestUri = new Uri(url),
Method = HttpMethod.Delete Method = HttpMethod.Delete
}; };
await FillHeaders(request.Headers);
HttpUtils.PrintRequest(client, request); HttpUtils.PrintRequest(client, request);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
request.Dispose(); request.Dispose();
@ -192,23 +149,64 @@ namespace LeanCloud.Storage.Internal.Http {
response.Dispose(); response.Dispose();
HttpUtils.PrintResponse(response, resultString); HttpUtils.PrintResponse(response, resultString);
HttpStatusCode statusCode = response.StatusCode;
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter()); Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
return; return;
} }
throw HandleErrorResponse(response.StatusCode, resultString);
}
LCException HandleErrorResponse(HttpStatusCode statusCode, string responseContent) {
int code = (int)statusCode; int code = (int)statusCode;
string message = resultString; string message = responseContent;
try { try {
// 尝试获取 LeanCloud 返回错误信息 // 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter()); Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent, new LeanCloudJsonConverter());
code = (int)error["code"]; code = (int)error["code"];
message = error["error"].ToString(); message = error["error"].ToString();
} catch (Exception e) { } catch (Exception e) {
Logger.Error(e.Message); Logger.Error(e.Message);
} finally {
throw new LCException(code, message);
} }
return new LCException(code, message);
}
string BuildUrl(string path, Dictionary<string, object> queryParams = null) {
string url = $"{server}/{apiVersion}/{path}";
if (queryParams != null) {
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
string queries = string.Join("&", queryPairs);
url = $"{url}?{queries}";
}
return url;
}
async Task FillHeaders(HttpRequestHeaders headers, Dictionary<string, object> additionalHeaders = null) {
// 额外 headers
if (additionalHeaders != null) {
foreach (KeyValuePair<string, object> kv in additionalHeaders) {
headers.Add(kv.Key, kv.Value.ToString());
}
}
// 签名
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
string data = $"{timestamp}{appKey}";
string hash = GetMd5Hash(md5, data);
string sign = $"{hash},{timestamp}";
headers.Add("X-LC-Sign", sign);
// 当前用户 Session Token
LCUser currentUser = await LCUser.GetCurrent();
if (currentUser != null) {
headers.Add("X-LC-Session", currentUser.SessionToken);
}
}
static string GetMd5Hash(MD5 md5Hash, string input) {
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; i++) {
sb.Append(data[i].ToString("x2"));
}
return sb.ToString();
} }
} }
} }