|
|
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 {
/// <summary></summary>
public static class WebUtility {
#region url
/// <summary>按 & 拆分多个参数。</summary>
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; }
/// <summary>获取 URL 查询段,不存在的段为 NULL 值。可要求解码。</summary>
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; }
/// <summary>获取参数并解码。</summary>
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<string>(); 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; }
/// <summary>指定可能的参数名,从 URL 中获取参数值并解码。</summary>
public static string GetParameter(Uri url, params string[] names) { return url == null ? null : GetParameter(url.Query, names); }
#endregion
#if NETFX || NETCORE
#region http
/// <summary>获取直连端的 IP。</summary>
public static string GetDirectIP(ApiRequest request) { if (request == null) return null; if (request.Context == null) return null; return GetDirectIP(request.Context.Request); }
/// <summary>获取直连端的 IP。</summary>
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
}
/// <summary>获取客户端 IP。</summary>
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); }
/// <summary>获取客户端 IP。</summary>
#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 d = GetDirectIP(request); var f = GetForwardedIP(request);
// return string.IsNullOrEmpty(f) ? "NULL" : $"{f},NULL";
// return string.IsNullOrEmpty(f) ? $"{d}" : $"{f},{d}";
// 没有代理,返回 Direct IP。
if (string.IsNullOrEmpty(f)) return d;
// 有代理,先加入 Forwarded IP,再加入 Direct IP。
var ip = f; if (withProxy && !string.IsNullOrEmpty(d)) ip = ip + "," + d;
return ip; }
/// <summary>请求来自于可信客户端。</summary>
public static bool FromTrusted(HttpRequest request, params string[] addresses) { if (request == null) return false;
// 解析可信地址。
var ips = new List<string>(); if (addresses != null) { foreach (var address in addresses) { var ip = address; if (!NetworkUtility.IsIP(ip)) ip = NetworkUtility.Resolve(ip); if (!NetworkUtility.IsIP(ip)) continue; if (ips.Contains(ip)) continue; ips.Add(ip); } }
// 检测代理 IP。
var headers = GetHeaders(request); var proxyIP = headers.GetValue("Ali-CDN-Real-IP", true).SafeTrim(); if (proxyIP.NotEmpty()) { var split = proxyIP.Split(' ', '|', ',', ':'); proxyIP = split.Length > 0 ? split[0] : null; } if (proxyIP.IsEmpty()) { proxyIP = headers.GetValue("X-Forwarded-For", true).SafeTrim(); if (proxyIP.NotEmpty()) { var split = proxyIP.Split(' ', '|', ',', ':'); proxyIP = split.Length > 0 ? split[0] : null; } } if (proxyIP.NotEmpty() && ips.Contains(proxyIP)) return true;
// 检测直连 IP,
var directIP = GetDirectIP(request); if (NetworkUtility.IsIP(directIP)) { if (proxyIP.IsEmpty()) { if (NetworkUtility.FromLAN(directIP)) return true; if (ips.Contains(directIP)) return true; } else { if (NetworkUtility.FromLAN(proxyIP) && NetworkUtility.FromLAN(directIP)) return true; }
}
return false; }
/// <summary>请求来自于可信客户端。</summary>
public static bool FromTrusted(HttpContext context, params string[] addresses) { if (context == null) return false; return FromTrusted(context.Request, addresses); }
/// <summary>请求来自于可信客户端。</summary>
public static bool FromTrusted(ApiRequest request, params string[] addresses) { if (request == null) return false; return FromTrusted(request.Context, addresses); }
/// <summary>获取 HTTP 方法。</summary>
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; }
/// <summary>获取 HTTP 方法。</summary>
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
/// <summary>获取请求的 URL,失败时返回 NULL 值。</summary>
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
}
/// <summary>获取 HTTP 头。</summary>
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<string, string>(key, value)); } } return sp; }
/// <summary>获取 X-Forwarded-For。</summary>
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; }
/// <summary>获取 X-Forwarded-For。</summary>
public static string GetForwardedIP(ApiRequest request) { if (request == null) return null; return GetForwardedIP(request.Headers); }
/// <summary>获取 X-Forwarded-For。</summary>
public static string GetForwardedIP(HttpRequest request) => GetForwardedIP(GetHeaders(request));
/// <summary>获取 User Agent。</summary>
public static string GetUserAgent(StringPairs headers) { if (headers == null) return null; return headers.GetValue("user-agent"); }
/// <summary>获取 User Agent。</summary>
public static string GetUserAgent(HttpRequest request) => GetUserAgent(GetHeaders(request));
#endregion
#region response
/// <summary>发送 302 状态,将客户端重新定向到新 URL。</summary>
public static void RedirectResponse(HttpResponse response, string url) { if (response == null) return; try { response.Redirect(url, true); } catch { } }
/// <summary>停止并关闭响应流。可指定向发送缓冲区的数据。</summary>
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
/// <summary>获取 URL 查询段,不存在的段为 NULL 值。可要求解码。</summary>
public static string GetSegmentalUrl(ApiRequest request, int index = 3, bool decode = false) { return request == null ? null : GetSegmental(request.Url, index, decode); }
/// <summary>获取参数。</summary>
public static string GetParameter(List<KeyValuePair<string, string>> parameters, params string[] names) { if (parameters == null || parameters.Count < 1) return null; if (names == null || names.Length < 1) return null;
var lowerNames = new List<string>(); 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; }
/// <summary>获取参数,指定可能的参数名,从 URL 中获取参数时将解码。</summary>
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<string>(); var lowerNames = new List<string>(); 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; }
/// <summary>转移 WebAPI 请求到其它服务器、应用或功能。</summary>
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();
/// <summary>获取日志文件路径发生错误时返回 NULL 值。</summary>
/// <remarks>d:\app\log\2020-02-02.log</remarks>
/// <remarks>d:\www\app_data\log\2020-02-02.log</remarks>
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; }
/// <summary>写入日志。</summary>
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
}
}
|