You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

712 lines
25 KiB

using Apewer;
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Apewer.Models;
using System.IO;
using System.Collections;
using Apewer.Source;
#if NETFX
using System.Web;
#endif
#if NETCORE
using Microsoft.AspNetCore.Http;
#endif
namespace Apewer.Web
{
/// <summary></summary>
public static class WebUtility
{
/// <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);
}
#if NETFX || NETCORE
/// <summary>获取程序目录的路径。</summary>
public static string AppDirectory
{
get
{
// AppDomain.CurrentDomain.BaseDirectory
// AppDomain.CurrentDomain.SetupInformation.ApplicationBase
return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
}
}
#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);
// 去除端口。
if (d.NotEmpty()) d = d.Split(':')[0];
if (f.NotEmpty()) f = f.Split(':')[0];
// 没有代理,返回 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
{
try
{
var values = request.Headers[key];
var text = values.ToString();
sp.Add(new KeyValuePair<string, string>(key, text));
}
catch { }
}
}
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
}
/// <summary>设置响应。</summary>
public static string Respond(ApiResponse response, Json data, bool lower = true)
{
if (response == null) return "Response 对象无效。";
if (data != null)
{
if (lower) data = Json.ToLower(data);
response.Data.Reset(data);
}
return null;
}
/// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
public static string Respond(ApiResponse response, IList list, bool lower = true, int depth = -1, bool force = false)
{
if (response == null) return "Response 对象无效。";
if (list == null)
{
var error = "列表对象无效。";
response.Error(error);
return error;
}
var json = Json.Parse(list, lower, depth, force);
if (json == null || !json.Available)
{
var error = "列表无法序列化。";
response.Error(error);
return error;
}
if (response.Data == null) response.Data = Json.NewObject();
response.Data.SetProperty("count", list.Count);
response.Data.SetProperty("list", Json.Parse(list, lower, depth, force));
return null;
}
/// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
public static string Respond(ApiResponse response, Record record, bool lower = true)
{
if (response == null) return "Response 对象无效。";
if (record == null)
{
var error = "记录无效。";
response.Error(error);
return error;
}
var json = Json.Parse(record, lower);
if (json == null || !json.Available)
{
var error = "记录无法序列化。";
response.Error(error);
return error;
}
if (response.Data == null) response.Data = Json.NewObject();
response.Data.Reset(json);
return null;
}
/// <summary>从 Response 头中移除 Server 属性。</summary>
public static void RemoveServer(HttpResponse response)
{
if (response == null) return;
#if NETFX
var keys = new List<string>(response.Headers.AllKeys);
#else
var keys = new List<string>(response.Headers.Keys);
#endif
if (keys.Contains("Server")) response.Headers.Remove("Server");
if (keys.Contains("X-Powered-By")) response.Headers.Remove("X-Powered-By");
}
#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 Cookies
/// <summary>获取 Cookies。</summary>
public static StringPairs GetCookies(HttpRequest request)
{
var sp = new StringPairs();
if (request != null && request.Cookies != null)
{
#if NETFX
foreach (var key in request.Cookies.AllKeys)
{
try
{
var cookie = request.Cookies[key];
var value = cookie.Value ?? "";
sp.Add(new KeyValuePair<string, string>(key, value));
}
catch { }
}
#else
foreach (var key in request.Cookies.Keys)
{
try
{
var value = request.Cookies[key] ?? "";
sp.Add(new KeyValuePair<string, string>(key, value));
}
catch { }
}
#endif
}
return sp;
}
/// <summary>设置响应的 Cookies。</summary>
public static string SetCookies(HttpResponse response, IEnumerable<KeyValuePair<string, string>> cookies)
{
// experimentation_subject_id
if (cookies == null) return "参数 Response 无效。";
var count = 0;
foreach (var kvp in cookies)
{
count++;
var error = SetCookie(response, kvp.Key, kvp.Value);
if (error.NotEmpty()) return error;
}
return null;
}
/// <summary>设置响应的 Cookie。</summary>
public static string SetCookie(HttpResponse response, string key, string value)
{
if (response == null) return null;
var k = key.SafeTrim();
var v = value.SafeTrim();
if (k.IsEmpty()) return "参数 Key 无效。";
try
{
#if NETFX
var cookie = new HttpCookie(k, v);
var now = DateTime.Now;
cookie.Expires = v.IsEmpty() ? now.AddDays(-1) : now.AddYears(1);
response.SetCookie(cookie);
#else
// var options = new CookieOptions();
// response.Cookies.Append(key, value, options);
response.Cookies.Append(key, value);
#endif
return null;
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion
#endif
}
}