using Apewer.Network; using System; using System.Collections.Generic; using System.Text; namespace Apewer.Web { /// 请求 API 框架的客户端。 public class ApiClient { private const int DefaultTimeout = 60000; /// 服务器 URL 根路径。 public virtual string UrlRoot { get; set; } /// 使用随机值,并验证响应。 public virtual bool UseRandom { get; set; } /// 账号令牌。 public virtual string Ticket { get; set; } /// 会话标识。 public virtual string Session { get; set; } /// 用户代理。 public virtual string UserAgent { get; set; } /// 请求超时,默认值为 60 秒。 public virtual int Timeout { get; set; } = DefaultTimeout; void Send(HttpClient client, string random, Action succeed = null, Action failed = null) { client.UserAgent = UserAgent; var ex = client.Send(); if (ex != null) { failed?.Invoke(ex.Message()); return; } if (succeed == null && failed == null) return; var body = client.ResponseData; if (body == null || body.LongLength < 1L) { failed?.Invoke("服务器返回了空数据。"); return; } var json = Json.From(body.Text()); if (!json) { failed?.Invoke("服务器返回了非 Json 格式的数据。"); return; } var status = json["status"]; if (status != "ok") { var message = json["message"]; failed?.Invoke(message); return; } var data = json.GetProperty("data"); succeed?.Invoke(data); } void Send(HttpClient client, string random, Action succeed = null, Action failed = null) { client.UserAgent = UserAgent; var ex = client.Send(); if (ex != null) { failed?.Invoke(ex.Message()); return; } if (succeed == null) return; var body = client.ResponseData; succeed?.Invoke(body); } string Random() => UseRandom ? TextUtility.Random(8) : null; #region get /// 发起 GET 请求,获取响应中的 data 属性。 public void Get(string urlPath, Action succeed = null, Action failed = null) { var random = Random(); var client = Get(urlPath); Send(client, random, succeed, failed); } /// 发起 GET 请求,获取响应的主体。 public void Get(string urlPath, Action succeed = null, Action failed = null) { var random = Random(); var client = Get(urlPath); Send(client, random, succeed, failed); } HttpClient Get(string urlPath) { var random = Random(); var client = new HttpClient(); client.Url = MergeUrl(urlPath, random); client.Method = HttpMethod.GET; client.Timeout = Timeout; return client; } /// 发起 POST 请求,上传指定数据,获取响应中的 data 属性。 public void Upload(string urlPath, byte[] bytes, Action succeed = null, Action failed = null) { var random = Random(); var client = Upload(urlPath, random, bytes); Send(client, random, succeed, failed); } /// 发起 POST 请求,上传指定数据,获取响应的主体。 public void Upload(string urlPath, byte[] bytes, Action succeed = null, Action failed = null) { var random = Random(); var client = Upload(urlPath, random, bytes); Send(client, random, succeed, failed); } HttpClient Upload(string urlPath, string random, byte[] bytes) { var client = new HttpClient(); client.Url = MergeUrl(urlPath, random); client.Method = HttpMethod.POST; client.Timeout = Timeout; client.RequestContentType = "appliction/octet-stream"; client.RequestData = bytes; return client; } string MergeUrl(string urlPath, string random) { var sb = new StringBuilder(); sb.Append(TextUtility.AssureEnds(UrlRoot, "/", true)); var query = null as string; if (!string.IsNullOrEmpty(urlPath)) { var offset = urlPath.IndexOf("#"); if (offset > -1) urlPath = urlPath.Substring(0, offset); offset = urlPath.IndexOf("?"); if (offset < 0) { sb.Append(urlPath); } else { sb.Append(urlPath.Substring(0, offset)); query = urlPath.Substring(offset + 1); } } var session = Session; var ticket = Ticket; var ps = ApiUtility.Parameters(query, false); if (!string.IsNullOrEmpty(random) && !ps.HasKey("random", true)) ps.Add("random", random); if (!string.IsNullOrEmpty(session) && !ps.HasKey("session", true)) ps.Add("session", session); if (!string.IsNullOrEmpty(ticket) && !ps.HasKey("ticket", true)) ps.Add("ticket", ticket); if (ps.Count > 0) { var first = true; foreach (var p in ps) { if (first) { sb.Append("?"); first = false; } else sb.Append("&"); sb.Append(p.Key); sb.Append("="); sb.Append(p.Value); } } var url = sb.ToString(); return url; } #endregion #region post /// 发起 POST 请求,获取响应中的 data 属性。 public void Post(string urlPath, Json data, Action succeed = null, Action failed = null) { var random = Random(); var client = Post(urlPath, random, null, null, data); Send(client, random, succeed, failed); } /// 发起 POST 请求,获取响应中的 data 属性。 public void Post(string application, string function, Json data, Action succeed = null, Action failed = null) { var random = Random(); var client = Post(null, random, application, function, data); Send(client, random, succeed, failed); } /// 发起 POST 请求,获取响应的主体。 public void Post(string urlPath, Json data, Action succeed = null, Action failed = null) { var random = Random(); var client = Post(urlPath, random, null, null, data); Send(client, random, succeed, failed); } /// 发起 POST 请求,获取响应的主体。 public void Post(string application, string function, Json data, Action succeed = null, Action failed = null) { var random = Random(); var client = Post(null, random, application, function, data); Send(client, random, succeed, failed); } HttpClient Post(string urlPath, string random, string application, string function, Json data) { var json = Json.NewObject(); if (!string.IsNullOrEmpty(application)) json.SetProperty("application", application); if (!string.IsNullOrEmpty(function)) json.SetProperty("function", function); if (!string.IsNullOrEmpty(random)) json.SetProperty("random", random); var session = Session; if (!string.IsNullOrEmpty(session)) json.SetProperty("session", session); var ticket = Ticket; if (!string.IsNullOrEmpty(random)) json.SetProperty("ticket", ticket); if (data != null) json.SetProperty("data", data); var url = TextUtility.AssureEnds(UrlRoot, "/", true); if (!string.IsNullOrEmpty(urlPath)) url += urlPath.StartsWith("/") ? urlPath.Substring(1) : urlPath; var client = new HttpClient(); client.Url = url; client.Method = HttpMethod.POST; client.Timeout = Timeout; client.RequestContentType = "application/json"; client.RequestData = json.ToString().Bytes(); return client; } #endregion } }