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.
321 lines
10 KiB
321 lines
10 KiB
using Apewer.Network;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
|
|
#if NETFX
|
|
using System.Web;
|
|
#endif
|
|
|
|
#if NETCORE
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Extensions.Hosting;
|
|
#endif
|
|
|
|
namespace Apewer.Web
|
|
{
|
|
|
|
/// <summary>API 调用器。</summary>
|
|
public sealed class ApiInvoker
|
|
{
|
|
|
|
private static ApiInvoker _default = new ApiInvoker();
|
|
|
|
/// <summary>默认的调用器。</summary>
|
|
public static ApiInvoker Default { get { return _default; } }
|
|
|
|
#region instance
|
|
|
|
private Dictionary<string, ApiApplication> _entries = null;
|
|
|
|
/// <summary>获取 API 入口的数量。</summary>
|
|
public int Entries { get { return _entries.Count; } }
|
|
|
|
private void AddEntries(Dictionary<string, ApiApplication> entries)
|
|
{
|
|
if (entries == null || entries.Count < 0) return;
|
|
|
|
var olds = _entries;
|
|
if (olds != null)
|
|
{
|
|
foreach (var old in olds)
|
|
{
|
|
if (entries.ContainsKey(old.Key)) continue;
|
|
entries.Add(old.Key, old.Value);
|
|
}
|
|
}
|
|
|
|
var sorted = Sort(entries);
|
|
_entries = sorted;
|
|
}
|
|
|
|
private void AddCurrent() => AddEntries(AppDomain.CurrentDomain.GetAssemblies());
|
|
|
|
/// <summary>清除所有入口。</summary>
|
|
public void ClearEntries() { _entries = null; }
|
|
|
|
/// <summary>添加指定程序集的入口。</summary>
|
|
public void AddEntries(IEnumerable<Assembly> assemblies) => AddEntries(GetEntries(assemblies));
|
|
|
|
/// <summary>添加指定程序集的入口。</summary>
|
|
public void AddEntries(Assembly assembly) => AddEntries(GetEntries(assembly));
|
|
|
|
private string Invoke(object context, Func<ApiProcessor> func)
|
|
{
|
|
if (context == null) return "Context 无效。";
|
|
|
|
if (_entries == null) AddEntries(AppDomain.CurrentDomain.GetAssemblies());
|
|
var entries = _entries;
|
|
// if (entries == null) return "Entries 无效。";
|
|
|
|
var processor = func();
|
|
processor.Invoker = this;
|
|
processor.Entries = entries;
|
|
return processor.Run();
|
|
}
|
|
|
|
/// <summary>执行。</summary>
|
|
public string Invoke(HttpListenerContext context) => Invoke(context, () => new ApiProcessorListener(context));
|
|
|
|
/// <summary>从 Listener 发起 API 调用。</summary>
|
|
public static string Listener(HttpListenerContext context) => _default.Invoke(context);
|
|
|
|
#if NETFX
|
|
|
|
/// <summary>执行。</summary>
|
|
public string Invoke(System.Web.HttpContext context) => Invoke(context, () => new ApiProcessorIIS(context));
|
|
|
|
/// <summary>从 IIS 发起 API 调用。</summary>
|
|
public static string IIS(HttpContext context) => _default.Invoke(context);
|
|
|
|
#endif
|
|
|
|
#if NETCORE
|
|
|
|
/// <summary>执行。</summary>
|
|
public string Invoke(Microsoft.AspNetCore.Http.HttpContext context) => Invoke(context, () => new ApiProcessorCore(context));
|
|
|
|
/// <summary>从 ASP.NET Core 发起 API 调用。</summary>
|
|
public static string AspNetCore(HttpContext context) => _default.Invoke(context);
|
|
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region entries
|
|
|
|
static Dictionary<string, ApiApplication> Sort(Dictionary<string, ApiApplication> entries)
|
|
{
|
|
var names = new List<string>();
|
|
names.AddRange(entries.Keys);
|
|
names.Sort();
|
|
|
|
var sorted = new Dictionary<string, ApiApplication>();
|
|
foreach (var name in names) sorted.Add(name, entries[name]);
|
|
return sorted;
|
|
}
|
|
|
|
static Dictionary<string, ApiApplication> GetEntries(Assembly assembly)
|
|
{
|
|
if (assembly == null) return new Dictionary<string, ApiApplication>();
|
|
|
|
var types = RuntimeUtility.GetTypes(assembly);
|
|
return GetEntries(types);
|
|
}
|
|
|
|
static Dictionary<string, ApiApplication> GetEntries(IEnumerable<Assembly> assemblies)
|
|
{
|
|
var deduplicates = new List<Assembly>();
|
|
foreach (var assembly in assemblies)
|
|
{
|
|
if (assembly == null) continue;
|
|
if (deduplicates.Contains(assembly)) continue;
|
|
deduplicates.Add(assembly);
|
|
}
|
|
|
|
var all = new Dictionary<string, ApiApplication>();
|
|
foreach (var assembly in deduplicates)
|
|
{
|
|
var types = RuntimeUtility.GetTypes(assembly);
|
|
var entries = GetEntries(types);
|
|
foreach (var entry in entries)
|
|
{
|
|
if (all.ContainsKey(entry.Key)) continue;
|
|
all.Add(entry.Key, entry.Value);
|
|
}
|
|
}
|
|
|
|
return all;
|
|
}
|
|
|
|
static Dictionary<string, ApiApplication> GetEntries(IEnumerable<Type> types)
|
|
{
|
|
if (types == null) return null;
|
|
var entries = new Dictionary<string, ApiApplication>();
|
|
foreach (var type in types)
|
|
{
|
|
var entry = GetEntry(type);
|
|
if (entry == null) continue;
|
|
if (entries.ContainsKey(entry.Name)) continue;
|
|
entries.Add(entry.Name, entry);
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
static ApiApplication GetEntry(Type type)
|
|
{
|
|
var application = GetApplication(type);
|
|
if (application == null) return null;
|
|
|
|
var methods = type.GetMethods();
|
|
var fes = new ObjectSet<ApiFunction>(true);
|
|
foreach (var method in methods)
|
|
{
|
|
var function = GetFunction(application, method);
|
|
if (function == null) continue;
|
|
fes[function.Name] = function;
|
|
}
|
|
|
|
var fns = new List<string>();
|
|
fns.AddRange(RuntimeUtility.GetOrigin(fes).Keys);
|
|
fns.Sort();
|
|
foreach (var i in fns) application.Functions.Add(i, fes[i]);
|
|
|
|
return application;
|
|
}
|
|
|
|
internal static ApiApplication GetApplication(Type type, bool checkAttribute = true)
|
|
{
|
|
if (type == null) return null;
|
|
|
|
// Check Type Properties
|
|
if (!type.IsClass) return null;
|
|
if (type.IsAbstract) return null;
|
|
if (type.IsGenericType) return null;
|
|
|
|
// Check Type ApiAttributes
|
|
var attributes = null as object[];
|
|
if (checkAttribute)
|
|
{
|
|
attributes = type.GetCustomAttributes(typeof(ApiAttribute), false);
|
|
if (attributes.Length < 1) return null;
|
|
}
|
|
|
|
// Check Base
|
|
if (!RuntimeUtility.IsInherits(type, typeof(ApiController))) return null;
|
|
|
|
// Entry
|
|
var entry = new ApiApplication();
|
|
entry.Type = type;
|
|
entry.Assembly = type.Assembly;
|
|
|
|
if (checkAttribute)
|
|
{
|
|
// Name
|
|
var api = (ApiAttribute)attributes[0];
|
|
var name = api.Name;
|
|
if (string.IsNullOrEmpty(name)) name = type.Name;
|
|
name = name.ToLower();
|
|
entry.Name = name;
|
|
|
|
// Caption
|
|
entry.Caption = api.Caption;
|
|
if (entry.Caption.Length < 1)
|
|
{
|
|
var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
|
|
if (captions.Length > 0)
|
|
{
|
|
var caption = (CaptionAttribute)captions[0];
|
|
entry.Caption = caption.Title;
|
|
entry.Description = caption.Description;
|
|
}
|
|
}
|
|
|
|
// Visible
|
|
entry.Visible = api.Visible;
|
|
if (entry.Visible)
|
|
{
|
|
if (type.ContainsAttribute<HiddenAttribute>(false))
|
|
{
|
|
entry.Visible = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
entry.Name = "";
|
|
entry.Caption = "";
|
|
entry.Visible = false;
|
|
}
|
|
|
|
// Independent
|
|
if (type.ContainsAttribute<IndependentAttribute>(false))
|
|
{
|
|
entry.Independent = true;
|
|
}
|
|
|
|
// Module
|
|
entry.Module = TextUtility.Join("-", entry.Assembly.GetName().Name, entry.Assembly.GetName().Version.ToString());
|
|
|
|
return entry;
|
|
}
|
|
|
|
static ApiFunction GetFunction(ApiApplication application, MethodInfo method)
|
|
{
|
|
if (method == null) return null;
|
|
|
|
// 滤除构造函数。
|
|
if (method.IsConstructor) return null;
|
|
|
|
// 滤除继承的方法。
|
|
if (method.DeclaringType.Equals(typeof(object))) return null;
|
|
if (method.DeclaringType.Equals(typeof(ApiController))) return null;
|
|
|
|
// 滤除带参数的方法。
|
|
var parameters = method.GetParameters();
|
|
if (parameters.Length > 0) return null;
|
|
|
|
// Entry
|
|
var entry = new ApiFunction();
|
|
entry.Type = application.Type;
|
|
entry.Assembly = application.Type.Assembly;
|
|
entry.Method = method;
|
|
entry.Name = method.Name.ToLower();
|
|
|
|
// Caption
|
|
var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
|
|
if (captions.Length > 0)
|
|
{
|
|
var caption = (CaptionAttribute)captions[0];
|
|
entry.Caption = caption.Title;
|
|
entry.Description = caption.Description;
|
|
}
|
|
|
|
// Visible
|
|
entry.Visible = true;
|
|
if (application.Visible)
|
|
{
|
|
var hidden = method.GetCustomAttributes(typeof(HiddenAttribute), false);
|
|
if (hidden.Length > 0) entry.Visible = false;
|
|
}
|
|
else
|
|
{
|
|
entry.Visible = false;
|
|
}
|
|
|
|
// Returnable
|
|
if (method.ReturnType.Equals(typeof(string)))
|
|
{
|
|
entry.Returnable = true;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|