using Apewer; using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using Apewer.Models; using System.IO; #if NETFX using System.Web; #endif #if NETCORE using Microsoft.AspNetCore.Http; #endif namespace Apewer.Web { /// public static class WebUtility { #region url /// 按 & 拆分多个参数。 public static StringPairs ParseParameters(string query, bool decode = true) { var input = query; var list = new StringPairs(); if (!string.IsNullOrEmpty(input)) { if (input[0] == '?') input = input.Substring(1); var args = input.Split('&'); foreach (var arg in args) { var equals = arg.IndexOf("="); var left = equals < 0 ? arg : arg.Substring(0, equals); var right = equals < 0 ? "" : arg.Substring(equals + 1); if (decode) { left = TextUtility.DecodeUrl(left); right = TextUtility.DecodeUrl(right); } list.Add(left, right); } } return list; } /// 获取 URL 查询段,不存在的段为 NULL 值。可要求解码。 public static string GetSegmental(Uri url, int index = 3, bool decode = false) { try { if (url == null) return null; if (index < 0) return null; var sus = url.AbsolutePath.Split('/'); if (sus.Length > index) { var result = sus[index]; if (decode) result = TextUtility.DecodeUrl(result); return result; } } catch { } return null; } /// 获取参数并解码。 public static string GetParameter(string encoded, params string[] names) { if (string.IsNullOrEmpty(encoded)) return null; if (names == null || names.Length < 1) return null; // Names 转为小写,加强适配。 var lowerNames = new List(); foreach (var name in names) { var lower = TextUtility.ToLower(name); if (string.IsNullOrEmpty(lower)) continue; lowerNames.Add(lower); } if (lowerNames.Count < 1) return null; // 分参数对比。 var parameters = ParseParameters(encoded); var matched = false; foreach (var parameter in parameters) { var left = parameter.Key; var right = parameter.Value; var lowerLeft = TextUtility.ToLower(left); if (lowerNames.Contains(right)) { matched = true; if (!string.IsNullOrEmpty(right)) return right; } } return matched ? "" : null; } /// 指定可能的参数名,从 URL 中获取参数值并解码。 public static string GetParameter(Uri url, params string[] names) { return url == null ? null : GetParameter(url.Query, names); } #endregion #if NETFX || NETCORE #region http /// 获取直连端的 IP。 public static string GetDirectIP(ApiRequest request) { if (request == null) return null; if (request.Context == null) return null; return GetDirectIP(request.Context.Request); } /// 获取直连端的 IP。 public static string GetDirectIP(HttpRequest request) { if (request == null) return null; #if NETFX return request.UserHostAddress; #else if (request.HttpContext == null || request.HttpContext.Connection == null) return null; return request.HttpContext.Connection.RemoteIpAddress.ToString(); #endif } /// 获取客户端 IP。 public static string GetClientIP(ApiRequest request, bool withProxy = false) { if (request == null) return null; if (request.Context == null) return null; return GetClientIP(request.Context.Request, withProxy); } /// 获取客户端 IP。 #if NETFX public static string GetClientIP(System.Web.HttpRequest request, bool withProxy = false) #else public static string GetClientIP(Microsoft.AspNetCore.Http.HttpRequest request, bool withProxy = false) #endif { if (request == null) return null; var direct = GetDirectIP(request); var x_forwarded_for = GetForwardedIP(request); if (string.IsNullOrEmpty(direct)) { return string.IsNullOrEmpty(x_forwarded_for) ? "NULL" : $"{x_forwarded_for},NULL"; } else { return string.IsNullOrEmpty(x_forwarded_for) ? $"{direct}" : $"{x_forwarded_for},{direct}"; } } /// 请求来自于可信客户端。 public static bool FromTrusted(HttpRequest request, params string[] addresses) { if (request == null) return false; // 检测直连 IP, var direct = GetDirectIP(request); if (!string.IsNullOrEmpty(direct)) { if (NetworkUtility.FromLAN(direct)) return true; } // 遍历传入的地址。 if (addresses != null && addresses.Length > 0) { // 读取 HTTP 头。 var forwarded = GetForwardedIP(request); if (!NetworkUtility.IsIP(forwarded)) return false; foreach (var address in addresses) { if (NetworkUtility.IsIP(address)) { if (forwarded == address) return true; } else { var ip = NetworkUtility.Resolve(address); if (NetworkUtility.IsIP(ip)) { if (forwarded == ip) return true; } } } } return false; } /// 请求来自于可信客户端。 public static bool FromTrusted(HttpContext context, params string[] addresses) { if (context == null) return false; return FromTrusted(context.Request, addresses); } /// 请求来自于可信客户端。 public static bool FromTrusted(ApiRequest request, params string[] addresses) { if (request == null) return false; return FromTrusted(request.Context, addresses); } /// 获取 HTTP 方法。 public static Network.HttpMethod GetMethod(string method) { if (!string.IsNullOrEmpty(method)) { var upper = TextUtility.ToUpper(method); if (upper.Contains("OPTIONS")) return Network.HttpMethod.OPTIONS; else if (upper.Contains("POST")) return Network.HttpMethod.POST; else if (upper.Contains("GET")) return Network.HttpMethod.GET; else if (upper.Contains("CONNECT")) return Network.HttpMethod.CONNECT; else if (upper.Contains("DELETE")) return Network.HttpMethod.DELETE; else if (upper.Contains("HEAD")) return Network.HttpMethod.HEAD; else if (upper.Contains("PATCH")) return Network.HttpMethod.PATCH; else if (upper.Contains("PUT")) return Network.HttpMethod.PUT; else if (upper.Contains("TRACE")) return Network.HttpMethod.TRACE; } return Network.HttpMethod.NULL; } /// 获取 HTTP 方法。 public static Network.HttpMethod GetMethod(HttpRequest request) { if (request == null) return Network.HttpMethod.NULL; #if NETFX return GetMethod(request.HttpMethod); #else return GetMethod(request.Method); #endif } #endregion #region request /// 获取请求的 URL,失败时返回 NULL 值。 public static Uri GetUrl(HttpRequest request) { if (request == null) return null; #if NETFX return request.Url; #else var context = request.HttpContext; if (context == null) return null; var https = request.IsHttps; var port = context.Connection.LocalPort; var query = request.QueryString == null ? null : request.QueryString.Value; var sb = new StringBuilder(); sb.Append(https ? "https://" : "http://"); sb.Append(request.Host.Host ?? ""); if ((https && port != 443) || (!https && port != 80)) { sb.Append(":"); sb.Append(port); } sb.Append(request.Path); if (!string.IsNullOrEmpty(query)) { sb.Append("?"); sb.Append(query); } var url = sb.ToString(); var uri = new Uri(url); return uri; #endif } /// 获取 HTTP 头。 public static StringPairs GetHeaders(HttpRequest request) { var sp = new StringPairs(); if (request != null && request.Headers != null) { #if NETFX foreach (var key in request.Headers.AllKeys) #else foreach (var key in request.Headers.Keys) #endif { var value = request.Headers[key].ToString(); sp.Add(new KeyValuePair(key, value)); } } return sp; } /// 获取 X-Forwarded-For。 public static string GetForwardedIP(StringPairs headers) { if (headers == null) return null; var value = headers.GetValue("x-forwarded-for"); if (!string.IsNullOrEmpty(value)) value = value.Split(":")[0]; return value; } /// 获取 X-Forwarded-For。 public static string GetForwardedIP(ApiRequest request) { if (request == null) return null; return GetForwardedIP(request.Headers); } /// 获取 X-Forwarded-For。 public static string GetForwardedIP(HttpRequest request) => GetForwardedIP(GetHeaders(request)); /// 获取 User Agent。 public static string GetUserAgent(StringPairs headers) { if (headers == null) return null; return headers.GetValue("user-agent"); } /// 获取 User Agent。 public static string GetUserAgent(HttpRequest request) => GetUserAgent(GetHeaders(request)); #endregion #region response /// 发送 302 状态,将客户端重新定向到新 URL。 public static void RedirectResponse(HttpResponse response, string url) { if (response == null) return; try { response.Redirect(url, true); } catch { } } /// 停止并关闭响应流。可指定向发送缓冲区的数据。 public static void StopResponse(HttpResponse response, bool flush = true) { if (response == null) return; #if NETFX try { if (flush) response.Flush(); } catch { } try { response.Close(); } catch { } // try { response.End(); } catch { } #endif } #endregion #region api #if NETFX /// 获取 URL 查询段,不存在的段为 NULL 值。可要求解码。 public static string GetSegmentalUrl(ApiRequest request, int index = 3, bool decode = false) { return request == null ? null : GetSegmental(request.Url, index, decode); } #endif /// 获取参数。 public static string GetParameter(List> parameters, params string[] names) { if (parameters == null || parameters.Count < 1) return null; if (names == null || names.Length < 1) return null; var lowerNames = new List(); foreach (var name in names) { var lower = TextUtility.ToLower(name); if (string.IsNullOrEmpty(lower)) continue; lowerNames.Add(lower); } foreach (var parameter in parameters) { var lowerKey = TextUtility.ToLower(parameter.Key); if (lowerNames.Contains(lowerKey)) { var value = parameter.Value; if (!string.IsNullOrEmpty(value)) return value; } } return null; } /// 获取参数,指定可能的参数名,从 URL 中获取参数时将解码。 public static string GetParameter(ApiRequest request, params string[] names) { if (request == null) return null; if (names == null || names.Length < 1) return null; var sameNames = new List(); var lowerNames = new List(); foreach (var name in names) { if (string.IsNullOrEmpty(name)) continue; if (!sameNames.Contains(name)) sameNames.Add(name); var lower = TextUtility.ToLower(name); if (string.IsNullOrEmpty(lower)) continue; if (!lowerNames.Contains(lower)) lowerNames.Add(lower); } // POST 优先。 var data = request.Data; if (data != null && data.IsObject) { var properties = data.GetProperties(); if (properties != null) { // Json 区分大小写,先全字匹配。 foreach (var property in properties) { if (!property.IsProperty) continue; if (sameNames.Contains(property.Name) && property.Value != null) { var value = property.Value; if (value == null) continue; var text = value.ToString(); if (!string.IsNullOrEmpty(text)) return text; } } // 以小写模糊匹配。 foreach (var property in properties) { if (!property.IsProperty) continue; var lowerName = TextUtility.ToLower(property.Name); if (lowerNames.Contains(lowerName) && property.Value != null) { var value = property.Value; if (value == null) continue; var text = value.ToString(); if (!string.IsNullOrEmpty(text)) return text; } } } } // 从已解析的 Parameters 中搜索。 if (request.Parameters != null) { var value = GetParameter(request.Parameters); if (!string.IsNullOrEmpty(value)) return value; } // GET 参数。 if (request.Parameters != null) { // 全字匹配。 foreach (var kvp in request.Parameters) { if (string.IsNullOrEmpty(kvp.Key) || string.IsNullOrEmpty(kvp.Value)) continue; if (sameNames.Contains(kvp.Key)) return kvp.Value; } // 以小写模糊匹配。 foreach (var kvp in request.Parameters) { var lowerKey = TextUtility.ToLower(kvp.Key); if (string.IsNullOrEmpty(lowerKey) || string.IsNullOrEmpty(kvp.Value)) continue; if (lowerNames.Contains(lowerKey)) return kvp.Value; } } return null; } /// 转移 WebAPI 请求到其它服务器、应用或功能。 public static string Transfer(ApiController controller, string url, string application = null, string function = null) { if (controller == null || controller.Request == null || controller.Response == null) return "ApiControllser 无效。"; if (url.IsEmpty()) return "ApiControllser 无效。"; var s = Json.NewObject(); s.SetProperty("random", TextUtility.NewGuid()); s.SetProperty("application", application.IsEmpty() ? controller.Request.Application : application); s.SetProperty("function", function.IsEmpty() ? controller.Request.Function : function); s.SetProperty("data", controller.Request.Data); s.SetProperty("session", controller.Request.Session); s.SetProperty("ticket", controller.Request.Ticket); s.SetProperty("page", controller.Request.Page); var c = Network.HttpClient.SimplePost(url, s.ToString().GetBytes()); var r = Json.Parse(c.Response.Data.GetString()); if (r == null || !r.Available) { controller.Response.Error("请求失败。"); return "请求失败。"; } if (r["status"] != "ok") { controller.Response.Error(r["message"]); return r["message"]; } controller.Response.Data.Reset(r.GetProperty("data")); return null; } #endregion #region Log private static object LogLocker = new object(); /// 获取日志文件路径发生错误时返回 NULL 值。 /// d:\app\log\2020-02-02.log /// d:\www\app_data\log\2020-02-02.log public static string GetLogPath() { // 找到 App_Data 目录。 var appDir = KernelUtility.ApplicationBasePath; var dataDir = Path.Combine(appDir, "app_data"); if (StorageUtility.DirectoryExists(dataDir)) appDir = dataDir; // 检查 Log 目录,不存在时创建,创建失败时返回。 var logDir = Path.Combine(appDir, "log"); if (!StorageUtility.AssureDirectory(logDir)) return null; // 文件不存在时创建新文件,无法创建时返回。 var date = DateTime.Now.ToLucid(true, false, false, false); var filePath = Path.Combine(logDir, date + ".log"); StorageUtility.CreateFile(filePath, 0, false); if (!StorageUtility.FileExists(filePath)) return null; // 返回 log 文件路径。 return filePath; } /// 写入日志。 public static string WriteLog(params object[] content) { lock (LogLocker) { var path = GetLogPath(); if (path.IsEmpty()) return "无法获取日志文件路径。"; var text = TextUtility.Join(" | ", content); text = TextUtility.Merge(DateTimeUtility.NowLucid, " ", text, "\r\n"); var bytes = TextUtility.ToBinary(text); if (!StorageUtility.AppendFile(path, bytes)) return "写日志文件失败。"; return null; } } #endregion #endif } }