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

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
}
}