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.
310 lines
11 KiB
310 lines
11 KiB
using Apewer.Network;
|
|
using Apewer.Source;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
|
|
using static Apewer.Internals.ApiHelper;
|
|
|
|
namespace Apewer.Web
|
|
{
|
|
|
|
internal class ApiProcessor
|
|
{
|
|
|
|
internal ApiInvoker Invoker;
|
|
internal ApiEntries Entries;
|
|
internal ApiProvider Provider;
|
|
ApiOptions Options;
|
|
|
|
Stopwatch Stopwatch;
|
|
|
|
Uri Url;
|
|
HttpMethod Method;
|
|
ApiRequest ApiRequest;
|
|
ApiResponse ApiResponse;
|
|
|
|
internal ApiProcessor()
|
|
{
|
|
}
|
|
|
|
/// <summary>执行处理程序,返回错误信息。</summary>
|
|
public string Run()
|
|
{
|
|
if (Options == null) Options = new ApiOptions();
|
|
if (Options.WithDuration)
|
|
{
|
|
Stopwatch = new Stopwatch();
|
|
Stopwatch.Start();
|
|
}
|
|
var error = Flow();
|
|
if (Stopwatch != null)
|
|
{
|
|
Stopwatch.Stop();
|
|
Stopwatch = null;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
string Flow()
|
|
{
|
|
try
|
|
{
|
|
// 传入字段。
|
|
if (Provider == null) return "服务程序无效。";
|
|
if (Invoker == null) return "调用器无效。";
|
|
if (Entries == null) return "入口无效。";
|
|
Options = Invoker.Options ?? new ApiOptions();
|
|
Provider.Options = Options;
|
|
|
|
// 检查执行的前提条件,获取 Method 和 URL。
|
|
var check = Check();
|
|
if (!string.IsNullOrEmpty(check)) return check;
|
|
|
|
// 准备请求和响应模型。
|
|
ApiRequest = GetRequest(Provider, Options, Method, Url);
|
|
ApiResponse = new ApiResponse();
|
|
ApiResponse.Random = ApiRequest.Random;
|
|
ApiResponse.Application = ApiRequest.Application;
|
|
ApiResponse.Function = ApiRequest.Function;
|
|
|
|
// 调用 API。
|
|
var invoke = Invoke();
|
|
if (!string.IsNullOrEmpty(invoke)) return invoke;
|
|
|
|
// 输出。
|
|
if (Stopwatch != null)
|
|
{
|
|
Stopwatch.Stop();
|
|
ApiResponse.Duration = Stopwatch.ElapsedMilliseconds;
|
|
Stopwatch = null;
|
|
}
|
|
Output(Provider, Options, ApiResponse, ApiRequest, Method);
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Stopwatch != null)
|
|
{
|
|
Stopwatch.Stop();
|
|
Stopwatch = null;
|
|
}
|
|
return ex.Message;
|
|
}
|
|
}
|
|
|
|
string Check()
|
|
{
|
|
// 服务程序检查。
|
|
var check = Provider.PreInvoke();
|
|
if (!string.IsNullOrEmpty(check)) return check;
|
|
|
|
// URL
|
|
Url = Provider.GetUrl();
|
|
if (Url == null) return "URL 无效。";
|
|
|
|
Method = Provider.GetMethod();
|
|
if (Method == HttpMethod.NULL) return "HTTP 方法无效。";
|
|
if (Method == HttpMethod.OPTIONS) return null;
|
|
|
|
// favicon.ico
|
|
var lowerPath = TextUtility.AssureStarts(TextUtility.Lower(Url.AbsolutePath), "/");
|
|
if (!Options.AllowFavIcon)
|
|
{
|
|
if (lowerPath.StartsWith("/favicon.ico"))
|
|
{
|
|
Output(Provider, Options, null, null);
|
|
return "已取消对 favicon.ico 的请求。";
|
|
}
|
|
}
|
|
|
|
// robots.txt
|
|
if (!Options.AllowRobots)
|
|
{
|
|
if (lowerPath.StartsWith("/robots.txt"))
|
|
{
|
|
const string text = "User-agent: *\nDisallow: / \n";
|
|
Output(Provider, Options, "text/plain", TextUtility.Bytes(text));
|
|
return "已取消对 robots.txt 的请求。";
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// 寻找入口。
|
|
string Invoke()
|
|
{
|
|
var appName = ApiRequest.Application;
|
|
var funcName = ApiRequest.Function;
|
|
var random = ApiRequest.Random;
|
|
|
|
Invoke(Entries.Get(appName));
|
|
|
|
if (Stopwatch != null) ApiResponse.Duration = Stopwatch.ElapsedMilliseconds;
|
|
ApiResponse.Application = appName;
|
|
ApiResponse.Function = funcName;
|
|
ApiResponse.Random = random;
|
|
|
|
return null;
|
|
}
|
|
|
|
// 创建控制器。
|
|
void Invoke(ApiApplication application)
|
|
{
|
|
var request = ApiRequest;
|
|
var response = ApiResponse;
|
|
var function = null as ApiFunction;
|
|
var controller = null as ApiController;
|
|
|
|
// Application 无效,尝试默认控制器和枚举。
|
|
if (application == null)
|
|
{
|
|
var @default = Options.Default;
|
|
if (@default == null)
|
|
{
|
|
// 没有指定默认控制器,尝试枚举。
|
|
response.Error("Invalid Application");
|
|
if (Options.AllowEnumerate) response.Data = Enumerate(Entries.Enumerate(), Options);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// 创建默认控制器。
|
|
try { controller = CreateController(@default, request, response, Options); }
|
|
catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 创建控制器时候会填充 Controller.Request 属性,可能导致 Request.Function 被篡改,所以在创建之前获取 Function。
|
|
function = application.Get(request.Function);
|
|
try { controller = CreateController(application.Type, request, response, Options); }
|
|
catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
|
|
}
|
|
if (controller == null) response.Error("创建控制器实例失败。");
|
|
else Invoke(controller, application, function, Options, request, response);
|
|
RuntimeUtility.Dispose(controller);
|
|
}
|
|
|
|
// 调用 Function。
|
|
static void Invoke(ApiController controller, ApiApplication application, ApiFunction function, ApiOptions options, ApiRequest request, ApiResponse response)
|
|
{
|
|
// 没有 ApiApplication,使用了 Options 中指定的默认控制器。
|
|
if (application == null)
|
|
{
|
|
application = new ApiApplication();
|
|
application.Independent = true;
|
|
application.Hidden = true;
|
|
}
|
|
|
|
try
|
|
{
|
|
// 控制器初始化。
|
|
var initializer = ApiUtility.GetInitialier(controller);
|
|
var match = initializer == null ? true : initializer.Invoke(controller);
|
|
if (!match) return;
|
|
if (application.Independent) return;
|
|
|
|
if (function != null)
|
|
{
|
|
var result = function.Method.Invoke(controller, ReadParameters(request, function));
|
|
if (result == null || function.Returnable == null) return;
|
|
var returnable = function.Returnable;
|
|
|
|
// 已明确字符串类型,视为提示错误。
|
|
if (returnable.Equals(typeof(string)))
|
|
{
|
|
var error = result as string;
|
|
if (!string.IsNullOrEmpty(error)) response.Error(error);
|
|
return;
|
|
}
|
|
|
|
// 已明确 Exception 类型,视为提示错误。
|
|
if (result is Exception)
|
|
{
|
|
ApiUtility.Exception(response, result as Exception);
|
|
return;
|
|
}
|
|
|
|
// 已明确 Json 类型。
|
|
if (result is Json)
|
|
{
|
|
response.Data = result as Json;
|
|
return;
|
|
}
|
|
|
|
// 已明确 Model 类型。
|
|
if (result is ApiModel)
|
|
{
|
|
response.Model = result as ApiModel;
|
|
return;
|
|
}
|
|
|
|
// 类型未知,尝试 ToJson 方法。
|
|
var tojson = result as IToJson;
|
|
if (tojson != null)
|
|
{
|
|
response.Data = tojson.ToJson();
|
|
return;
|
|
}
|
|
|
|
// 类型未知,尝试 Record 模型。
|
|
var record = result as IRecord;
|
|
if (record != null)
|
|
{
|
|
response.Data = Json.From(record);
|
|
return;
|
|
}
|
|
|
|
// 未知类型,尝试 Json 类型。
|
|
var json = result as Json;
|
|
if (json != null)
|
|
{
|
|
response.Data = json;
|
|
return;
|
|
}
|
|
|
|
// 未知返回类型,无法明确输出格式,忽略。
|
|
}
|
|
else
|
|
{
|
|
// 未匹配到 Function,尝试 Default。
|
|
var @default = ApiUtility.GetDefault(controller);
|
|
if (@default != null)
|
|
{
|
|
@default.Invoke(controller);
|
|
return;
|
|
}
|
|
|
|
// 没有执行任何 Function,尝试枚举。
|
|
if (application.Hidden)
|
|
{
|
|
response.Error("Invalid Application");
|
|
}
|
|
else
|
|
{
|
|
response.Error("Invalid Function");
|
|
if (options.AllowEnumerate) response.Data = Enumerate(application.Items, options);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
if (exception == null) response.Error("发生了未定义的异常。");
|
|
else ApiUtility.Exception(response, exception);
|
|
}
|
|
}
|
|
|
|
static ApiController CreateController(Type type, ApiRequest request, ApiResponse response, ApiOptions options)
|
|
{
|
|
var controller = (ApiController)Activator.CreateInstance(type);
|
|
ApiUtility.SetProperties(controller, request, response, options);
|
|
return controller;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|