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.
206 lines
7.1 KiB
206 lines
7.1 KiB
using Apewer.Web;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
using static Apewer.NumberUtility;
|
|
using static Apewer.Web.ApiUtility;
|
|
|
|
namespace Apewer.AspNetBridge
|
|
{
|
|
|
|
/// <summary>桥接 ASP.NET 的控制器。</summary>
|
|
public class BridgeController : Web.ApiController
|
|
{
|
|
|
|
#region static routes
|
|
|
|
static RouteItem[] _routes = null;
|
|
|
|
/// <summary>导出所有路由项。</summary>
|
|
public static Json ExportRoutes()
|
|
{
|
|
var array = Json.NewArray();
|
|
if (_routes != null)
|
|
{
|
|
foreach (var route in _routes)
|
|
{
|
|
var item = route.ToJson();
|
|
array.AddItem(item);
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
|
|
/// <summary>初始化路由。</summary>
|
|
/// <param name="assemblies">包含控制器的程序集。</param>
|
|
/// <param name="withVoid">包含返回 System.Void 的方法。</param>
|
|
public static void Initialize(IEnumerable<Assembly> assemblies, bool withVoid = false) => _routes = RouteItem.Parse(assemblies, withVoid);
|
|
|
|
/// <summary>初始化路由,不包含返回 System.Void 的方法。</summary>
|
|
/// <param name="assemblies">包含控制器的程序集。</param>
|
|
public static void Initialize(params Assembly[] assemblies) => _routes = RouteItem.Parse(assemblies, false);
|
|
|
|
#endregion
|
|
|
|
#region events
|
|
|
|
/// <summary>发生异常时候的处理程序。</summary>
|
|
public static OnException OnException { get; set; }
|
|
|
|
/// <summary>输出异常。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static void Output(ApiRequest request, ApiResponse response, MethodInfo method, Exception exception)
|
|
{
|
|
if (request == null) throw new ArgumentNullException(nameof(request));
|
|
if (response == null) throw new ArgumentNullException(nameof(response));
|
|
if (method == null) throw new ArgumentNullException(nameof(method));
|
|
if (exception == null) throw new ArgumentNullException(nameof(exception));
|
|
|
|
var sb = new StringBuilder();
|
|
if (request.IP.NotEmpty())
|
|
{
|
|
sb.Append(request.IP);
|
|
sb.Append(" ");
|
|
}
|
|
sb.Append(request.Method.ToString());
|
|
if (request.Url != null)
|
|
{
|
|
sb.Append(" ");
|
|
sb.Append(request.Url.OriginalString);
|
|
}
|
|
if (method != null)
|
|
{
|
|
sb.Append("\r\n");
|
|
sb.Append(method.DeclaringType.FullName);
|
|
sb.Append(".");
|
|
sb.Append(method.Name);
|
|
}
|
|
sb.Append("\r\n\r\n");
|
|
sb.Append(new ApiExceptionModel(exception).ToString());
|
|
|
|
var model = new ApiTextModel(sb.ToString());
|
|
model.Status = 500;
|
|
response.Model = model;
|
|
}
|
|
|
|
/// <summary>使用格式化程序处理所有响应。</summary>
|
|
/// <remarks>
|
|
/// <para>默认值:False( Bridge )</para>
|
|
/// <para>True:所有响应均使用自定义格式化程序,忽略 Bridge 内置程序。</para>
|
|
/// <para>False:优先使用 Bridge 内置程序格式化,对不支持的类型使用自定义程序。</para>
|
|
/// </remarks>
|
|
public static bool FormatAll { get; set; }
|
|
|
|
/// <summary>自定义格式化程序。</summary>
|
|
public static Func<object, ApiModel> Formatter { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region instance
|
|
|
|
/// <summary></summary>
|
|
public BridgeController() : base((c) => ((BridgeController)c).Execute(), (c) => ((BridgeController)c).Default()) { }
|
|
|
|
bool Execute()
|
|
{
|
|
if (_routes == null) Initialize();
|
|
|
|
var routes = _routes;
|
|
var route = RouteItem.Match(routes, Request.Url.AbsolutePath, Request.Method);
|
|
try
|
|
{
|
|
Execute(route);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Write("Route Error", ex.InnerException.GetType().FullName, ex.InnerException.Message);
|
|
var action = OnException;
|
|
if (action != null) action.Invoke(Request, Response, route.Method, ex.InnerException);
|
|
else Response.Model = new ApiStatusModel(500);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Default() { }
|
|
|
|
void Execute(RouteItem route)
|
|
{
|
|
if (route == null)
|
|
{
|
|
Response.Model = new ApiStatusModel(404);
|
|
return;
|
|
}
|
|
|
|
// 检查 HTTP 方法
|
|
switch (Request.Method)
|
|
{
|
|
case Network.HttpMethod.GET:
|
|
if (!route.Get)
|
|
{
|
|
Response.Model = new ApiStatusModel(405);
|
|
return;
|
|
}
|
|
break;
|
|
case Network.HttpMethod.POST:
|
|
if (!route.Post)
|
|
{
|
|
Response.Model = new ApiStatusModel(405);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
Response.Model = new ApiStatusModel(405);
|
|
return;
|
|
}
|
|
|
|
// 准备参数。
|
|
var pis = CollectionUtility.Vacuum(route.Parameters.Map(x => ApiParameter.Parse(x)));
|
|
var ps = ReadParameters(Request, route.Parameters);
|
|
|
|
// 准备控制器。
|
|
var c = Activator.CreateInstance(route.Controller) as ApiController;
|
|
c.Request = Request;
|
|
c.Response = Response;
|
|
|
|
// 调用 API 方法。
|
|
var r = route.Method.Invoke(c, ps);
|
|
|
|
// 格式化、返回。
|
|
if (route.Return == null || route.Return.Equals(typeof(void))) return;
|
|
Response.Model = Format(r) ?? new ApiStatusModel(204);
|
|
}
|
|
|
|
static ApiModel Format(object value)
|
|
{
|
|
var isNull = value.IsNull();
|
|
|
|
var all = FormatAll;
|
|
var formatter = Formatter;
|
|
var customized = formatter != null;
|
|
|
|
// 使用自定义格式化程序处理所有响应。
|
|
if (all && customized) return formatter.Invoke(value);
|
|
|
|
// 使用默认格式化。
|
|
if (value is ApiModel model) return model;
|
|
if (value is string text) return new ApiTextModel(text);
|
|
if (value is byte[] bytes) return new ApiBytesModel(bytes);
|
|
if (value is Json json) return new ApiJsonModel(json);
|
|
if (value is HttpActionResult har) return har.ToModel();
|
|
if (value is HttpResponseMessage hrm) return hrm.ToModel();
|
|
|
|
// 指定了自定义格式化程序。
|
|
if (customized) return formatter.Invoke(value);
|
|
|
|
// 默认。
|
|
return isNull ? null : new ApiTextModel(value.ToString());
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|