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.
		
		
		
		
		
			
		
			
				
					
					
						
							808 lines
						
					
					
						
							29 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							808 lines
						
					
					
						
							29 KiB
						
					
					
				
								using Apewer.Network;
							 | 
						|
								using System;
							 | 
						|
								using System.Collections.Generic;
							 | 
						|
								using System.Net;
							 | 
						|
								using System.Reflection;
							 | 
						|
								using static Apewer.Web.ApiUtility;
							 | 
						|
								
							 | 
						|
								namespace Apewer.Web
							 | 
						|
								{
							 | 
						|
								
							 | 
						|
								    internal class ApiProcessor
							 | 
						|
								    {
							 | 
						|
								
							 | 
						|
								        private ApiContext _context = null;
							 | 
						|
								
							 | 
						|
								        internal ApiProcessor(ApiContext context) => _context = context ?? throw new ArgumentNullException(nameof(context));
							 | 
						|
								
							 | 
						|
								        #region prepare
							 | 
						|
								
							 | 
						|
								        /// <summary>执行处理程序,返回错误信息。</summary>
							 | 
						|
								        public void Run()
							 | 
						|
								        {
							 | 
						|
								            var url = null as Uri;
							 | 
						|
								            var method = HttpMethod.NULL;
							 | 
						|
								            var response = null as ApiResponse;
							 | 
						|
								
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                // 检查执行的前提条件,获取 Method 和 URL。
							 | 
						|
								                var check = Check(ref method, ref url);
							 | 
						|
								                if (!string.IsNullOrEmpty(check))
							 | 
						|
								                {
							 | 
						|
								                    Logger.Internals.Error(typeof(ApiInvoker), check);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 准备请求模型。
							 | 
						|
								                var request = GetRequest(_context.Provider, _context.Options, method, url);
							 | 
						|
								                _context.Request = request;
							 | 
						|
								
							 | 
						|
								                // 准备响应模型。
							 | 
						|
								                response = new ApiResponse();
							 | 
						|
								                response.Random = request.Random;
							 | 
						|
								                response.Application = request.Application;
							 | 
						|
								                response.Function = request.Function;
							 | 
						|
								                _context.Response = response;
							 | 
						|
								
							 | 
						|
								                // OPTIONS
							 | 
						|
								                if (_context.Request.Method == HttpMethod.OPTIONS)
							 | 
						|
								                {
							 | 
						|
								                    if (!_context.Options.AllowOptions)
							 | 
						|
								                    {
							 | 
						|
								                        _context.Response.Model = new ApiTextModel("");
							 | 
						|
								                        return;
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 中间件
							 | 
						|
								                var middlewares = _context.Invoker.Middlewares;
							 | 
						|
								                if (middlewares.Length > 0)
							 | 
						|
								                {
							 | 
						|
								                    // 设置队列和回调。
							 | 
						|
								                    _mw_queue = new Queue<ApiMiddleware>(middlewares);
							 | 
						|
								                    _context.SetMiddlewareCallback(MiddlewareNext);
							 | 
						|
								
							 | 
						|
								                    // 执行。
							 | 
						|
								                    MiddlewareNext(_context);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    // 无中间件。
							 | 
						|
								                    Route();
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex)
							 | 
						|
								            {
							 | 
						|
								                ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
							 | 
						|
								                var message = ex.Message();
							 | 
						|
								                Logger.Internals.Error(typeof(ApiInvoker), message);
							 | 
						|
								            }
							 | 
						|
								            finally
							 | 
						|
								            {
							 | 
						|
								                // 输出。
							 | 
						|
								                if (response != null)
							 | 
						|
								                {
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        response.Duration = Duration(_context.Beginning);
							 | 
						|
								                        Output(_context.Provider, _context.Options, response, null, method);
							 | 
						|
								                    }
							 | 
						|
								                    catch { }
							 | 
						|
								                    finally
							 | 
						|
								                    {
							 | 
						|
								                        RuntimeUtility.Dispose(response.Model);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        static string Duration(DateTime beginning)
							 | 
						|
								        {
							 | 
						|
								            var span = DateTime.Now - beginning;
							 | 
						|
								            var ms = span.TotalMilliseconds;
							 | 
						|
								            if (ms < 1000) return Math.Round(ms, 0).ToString() + "ms";
							 | 
						|
								            if (ms < 10000) return Math.Round(ms / 1000, 2).ToString() + "s";
							 | 
						|
								            if (ms < 60000) return Math.Round(ms / 1000, 1).ToString() + "s";
							 | 
						|
								            return Math.Round(ms / 1000, 0).ToString() + "s";
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        string Check(ref HttpMethod method, ref Uri url)
							 | 
						|
								        {
							 | 
						|
								            // 服务程序检查。
							 | 
						|
								            var check = _context.Provider.PreInvoke();
							 | 
						|
								            if (!string.IsNullOrEmpty(check)) return check;
							 | 
						|
								
							 | 
						|
								            // URL
							 | 
						|
								            url = _context.Provider.GetUrl();
							 | 
						|
								            if (url == null) return "URL 无效。";
							 | 
						|
								
							 | 
						|
								            // Method
							 | 
						|
								            method = _context.Provider.GetMethod();
							 | 
						|
								            switch (method)
							 | 
						|
								            {
							 | 
						|
								                case HttpMethod.NULL:
							 | 
						|
								                    return "HTTP 方法无效。";
							 | 
						|
								                case HttpMethod.OPTIONS:
							 | 
						|
								                    if (!_context.Options.AllowOptions) return null;
							 | 
						|
								                    break;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // favicon.ico
							 | 
						|
								            var lowerPath = TextUtility.AssureStarts(TextUtility.Lower(url.AbsolutePath), "/");
							 | 
						|
								            if (!_context.Options.AllowFavIcon)
							 | 
						|
								            {
							 | 
						|
								                if (lowerPath.StartsWith("/favicon.ico"))
							 | 
						|
								                {
							 | 
						|
								                    Output(_context.Provider, _context.Options, null, null, null);
							 | 
						|
								                    return "已取消对 favicon.ico 的请求。";
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // robots.txt
							 | 
						|
								            if (!_context.Options.AllowRobots)
							 | 
						|
								            {
							 | 
						|
								                if (lowerPath.StartsWith("/robots.txt"))
							 | 
						|
								                {
							 | 
						|
								                    const string text = "User-agent: *\nDisallow: / \n";
							 | 
						|
								                    Output(_context.Provider, _context.Options, null, "text/plain", TextUtility.Bytes(text));
							 | 
						|
								                    return "已取消对 robots.txt 的请求。";
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return null;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region common
							 | 
						|
								
							 | 
						|
								        static Type Void = typeof(void);
							 | 
						|
								
							 | 
						|
								        // 创建控制器实例
							 | 
						|
								        static ApiController CreateController(Type type, ApiContext context)
							 | 
						|
								        {
							 | 
						|
								            var controller = (ApiController)Activator.CreateInstance(type);
							 | 
						|
								            ApiUtility.SetContext(controller, context);
							 | 
						|
								            return controller;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        static void Invoke(ApiContext context, MethodInfo method, ApiParameter[] parameters)
							 | 
						|
								        {
							 | 
						|
								            context.MethodInfo = method;
							 | 
						|
								
							 | 
						|
								            // 调用。
							 | 
						|
								            var parametersValue = ReadParameters(context.Request, parameters);
							 | 
						|
								            var controller = context.Controller;
							 | 
						|
								            var returnValue = method.Invoke(controller, parametersValue);
							 | 
						|
								
							 | 
						|
								            // 程序要求停止输出。
							 | 
						|
								            var response = context.Response;
							 | 
						|
								            if (response.StopReturn) return;
							 | 
						|
								
							 | 
						|
								            // 已经有了返回模型。
							 | 
						|
								            if (response.Model != null) return;
							 | 
						|
								
							 | 
						|
								            // 没有返回类型。
							 | 
						|
								            var returnType = method.ReturnType;
							 | 
						|
								            if (returnType == null || returnType.Equals(Void)) return;
							 | 
						|
								
							 | 
						|
								            // 已明确字符串类型。
							 | 
						|
								            if (returnType.Equals(typeof(string)))
							 | 
						|
								            {
							 | 
						|
								                var textValue = returnValue as string;
							 | 
						|
								                var textRenderer = context.Options.TextRenderer;
							 | 
						|
								                if (textRenderer != null)
							 | 
						|
								                {
							 | 
						|
								                    textRenderer.Invoke(context, textValue);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 默认视为提示错误
							 | 
						|
								                if (!string.IsNullOrEmpty(textValue)) response.Error(textValue);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 已明确 Exception 类型,视为提示错误。
							 | 
						|
								            if (returnValue is Exception)
							 | 
						|
								            {
							 | 
						|
								                ApiUtility.Exception(response, returnValue as Exception);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 已明确 Json 类型。
							 | 
						|
								            if (returnValue is Json json)
							 | 
						|
								            {
							 | 
						|
								                var renderer = context.Options.JsonRenderer;
							 | 
						|
								                if (renderer != null)
							 | 
						|
								                {
							 | 
						|
								                    renderer.Invoke(context, json);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 默认设置到 data 属性。
							 | 
						|
								                response.Data = json;
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 已明确 Model 类型。
							 | 
						|
								            if (returnValue is IApiModel model)
							 | 
						|
								            {
							 | 
						|
								                response.Model = model;
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 已明确 Result 类型。
							 | 
						|
								            if (returnValue is IActionResult result)
							 | 
						|
								            {
							 | 
						|
								                response.Model = result;
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 类型未知,尝试 ToJson 方法。
							 | 
						|
								            if (returnValue is IToJson toJson)
							 | 
						|
								            {
							 | 
						|
								                var tojson = toJson.ToJson();
							 | 
						|
								
							 | 
						|
								                var renderer = context.Options.JsonRenderer;
							 | 
						|
								                if (renderer != null)
							 | 
						|
								                {
							 | 
						|
								                    renderer.Invoke(context, tojson);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								                response.Data = tojson;
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 未知返回类型,尝试使用默认渲染器。
							 | 
						|
								            var defaultRenderer = context.Options.DefaultRenderer;
							 | 
						|
								            if (defaultRenderer != null) defaultRenderer.Invoke(context, returnValue);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region middleware
							 | 
						|
								
							 | 
						|
								        Queue<ApiMiddleware> _mw_queue = null;
							 | 
						|
								
							 | 
						|
								        void MiddlewareNext(ApiContext context)
							 | 
						|
								        {
							 | 
						|
								            if (_mw_queue.Count < 1)
							 | 
						|
								            {
							 | 
						|
								                Route();
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 创建下一个中间件的实例
							 | 
						|
								            var middleware = _mw_queue.Dequeue();
							 | 
						|
								            if (middleware.Type != null)
							 | 
						|
								            {
							 | 
						|
								                var instance = Activator.CreateInstance(middleware.Type) as IApiMiddleware;
							 | 
						|
								                if (instance == null) throw new Exception($"类型【{middleware.Type.FullName}】不是有效的中间件。");
							 | 
						|
								
							 | 
						|
								                // 调用
							 | 
						|
								                instance.Invoke(context);
							 | 
						|
								            }
							 | 
						|
								            else if (middleware.Callback != null)
							 | 
						|
								            {
							 | 
						|
								                middleware.Callback.Invoke(context, () => MiddlewareNext(context));
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                MiddlewareNext(context);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // 执行路由。
							 | 
						|
								        void Route()
							 | 
						|
								        {
							 | 
						|
								            // 路由
							 | 
						|
								            if (_context.Options.UseRoute)
							 | 
						|
								            {
							 | 
						|
								                var path = _context?.Request?.Url?.AbsolutePath;
							 | 
						|
								                path = path.TrimEnd('/');
							 | 
						|
								                var action = _context.Entries.GetAction(path);
							 | 
						|
								                if (action != null)
							 | 
						|
								                {
							 | 
						|
								                    _context.ApiAction = action;
							 | 
						|
								                    InvokeAction(action);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 反射
							 | 
						|
								            if (_context.Options.UseReflection)
							 | 
						|
								            {
							 | 
						|
								                var appName = _context.Request.Application;
							 | 
						|
								                var application = _context.Entries.GetApplication(appName);
							 | 
						|
								                InvokeApplication(application);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 未匹配到
							 | 
						|
								            _context.Response.Duration = Duration(_context.Beginning);
							 | 
						|
								            _context.Response.Model = new ApiStatusModel(404);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region route
							 | 
						|
								
							 | 
						|
								        // 执行 Action。
							 | 
						|
								        void InvokeAction(ApiAction action)
							 | 
						|
								        {
							 | 
						|
								            var controller = null as ApiController;
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                // 准备控制器。
							 | 
						|
								                controller = CreateController(action.Type, _context);
							 | 
						|
								
							 | 
						|
								                // 准备参数。
							 | 
						|
								                var parameters = action.Parameters;
							 | 
						|
								                var values = ReadParameters(_context.Request, parameters);
							 | 
						|
								
							 | 
						|
								                // 调用。
							 | 
						|
								                _context.Controller = controller;
							 | 
						|
								                Invoke(_context, action.MethodInfo, action.Parameters);
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex)
							 | 
						|
								            {
							 | 
						|
								                if (ex.InnerException != null) ex = ex.InnerException;
							 | 
						|
								                ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
							 | 
						|
								
							 | 
						|
								                var catcher = _context.Invoker.Catcher;
							 | 
						|
								                if (catcher != null)
							 | 
						|
								                {
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        var apiCatch = new ApiCatch(_context, ex);
							 | 
						|
								                        catcher.Invoke(apiCatch);
							 | 
						|
								                    }
							 | 
						|
								                    catch { }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            finally
							 | 
						|
								            {
							 | 
						|
								                RuntimeUtility.Dispose(controller);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region reflection
							 | 
						|
								
							 | 
						|
								        // 创建控制器。
							 | 
						|
								        void InvokeApplication(ApiApplication application)
							 | 
						|
								        {
							 | 
						|
								            var options = _context.Options;
							 | 
						|
								            var entries = _context.Entries;
							 | 
						|
								            var request = _context.Request;
							 | 
						|
								            var response = _context.Response;
							 | 
						|
								
							 | 
						|
								            // Application 无效,尝试默认控制器和枚举。
							 | 
						|
								            if (application == null)
							 | 
						|
								            {
							 | 
						|
								                var @default = options.Default;
							 | 
						|
								                if (@default == null)
							 | 
						|
								                {
							 | 
						|
								                    // 没有指定默认控制器,尝试枚举。
							 | 
						|
								                    response.Status = "notfound";
							 | 
						|
								                    response.Message = "Not Found";
							 | 
						|
								                    if (options.AllowEnumerate) response.Data = Enumerate(entries.Applications, options);
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    // 创建默认控制器。
							 | 
						|
								                    var controller = null as ApiController;
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        controller = CreateController(@default, _context);
							 | 
						|
								                        InvokeFunction(controller, application, null, options, request, response);
							 | 
						|
								                    }
							 | 
						|
								                    catch (Exception ex)
							 | 
						|
								                    {
							 | 
						|
								                        ApiUtility.Exception(response, ex.InnerException ?? ex);
							 | 
						|
								                    }
							 | 
						|
								                    finally
							 | 
						|
								                    {
							 | 
						|
								                        RuntimeUtility.Dispose(controller);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                // 创建控制器时候会填充 Controller.Request 属性,可能导致 Request.Function 被篡改,所以在创建之前获取 Function。
							 | 
						|
								                var function = application.GetFunction(request.Function);
							 | 
						|
								                var controller = null as ApiController;
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    controller = CreateController(application.Type, _context);
							 | 
						|
								                    InvokeFunction(controller, application, function, options, request, response);
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex)
							 | 
						|
								                {
							 | 
						|
								                    ApiUtility.Exception(response, ex.InnerException ?? ex);
							 | 
						|
								                }
							 | 
						|
								                finally
							 | 
						|
								                {
							 | 
						|
								                    RuntimeUtility.Dispose(controller);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // 调用 Function。
							 | 
						|
								        void InvokeFunction(ApiController controller, ApiApplication application, ApiFunction function, ApiOptions options, ApiRequest request, ApiResponse response)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                // 控制器初始化。
							 | 
						|
								                var initializer = ApiUtility.GetInitialier(controller);
							 | 
						|
								                var match = initializer == null ? true : initializer.Invoke(controller);
							 | 
						|
								                if (!match) return;
							 | 
						|
								                if (application.Independent) return;
							 | 
						|
								
							 | 
						|
								                if (function != null)
							 | 
						|
								                {
							 | 
						|
								                    // 调用 API,获取返回值。
							 | 
						|
								                    _context.Controller = controller;
							 | 
						|
								                    Invoke(_context, function.Method, function.Parameters);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    // 未匹配到 Function,尝试 Default。
							 | 
						|
								                    var @default = ApiUtility.GetDefault(controller);
							 | 
						|
								                    if (@default != null)
							 | 
						|
								                    {
							 | 
						|
								                        @default.Invoke(controller);
							 | 
						|
								                        return;
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    // 没有执行任何 Function,尝试枚举。
							 | 
						|
								                    response.Status = "notfound";
							 | 
						|
								                    if (application.Hidden)
							 | 
						|
								                    {
							 | 
						|
								                        response.Message = "Not Found";
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        response.Message = "Not Found";
							 | 
						|
								                        if (options.AllowEnumerate) response.Data = Enumerate(application.Functions, options);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex)
							 | 
						|
								            {
							 | 
						|
								                if (ex.InnerException != null) ex = ex.InnerException;
							 | 
						|
								                ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
							 | 
						|
								
							 | 
						|
								                var catcher = _context.Invoker.Catcher;
							 | 
						|
								                if (catcher != null)
							 | 
						|
								                {
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        var apiCatch = new ApiCatch(_context, ex);
							 | 
						|
								                        catcher.Invoke(apiCatch);
							 | 
						|
								                    }
							 | 
						|
								                    catch { }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region static
							 | 
						|
								
							 | 
						|
								        internal static ApiRequest GetRequest(ApiProvider provider, ApiOptions options, HttpMethod method, Uri url)
							 | 
						|
								        {
							 | 
						|
								            // 创建数据对象。
							 | 
						|
								            var request = new ApiRequest();
							 | 
						|
								
							 | 
						|
								            // Http Method。
							 | 
						|
								            request.Method = method;
							 | 
						|
								
							 | 
						|
								            // 基本信息。
							 | 
						|
								            var ip = provider.GetClientIP();
							 | 
						|
								            var headers = provider.GetHeaders() ?? new HttpHeaders();
							 | 
						|
								            request.Headers = headers;
							 | 
						|
								            request.IP = ip;
							 | 
						|
								            request.Url = url;
							 | 
						|
								            request.Referrer = provider.GetReferrer();
							 | 
						|
								            request.Parameters = ApiUtility.Parameters(url.Query);
							 | 
						|
								
							 | 
						|
								            // Headers。
							 | 
						|
								            request.UserAgent = ApiUtility.UserAgent(headers);
							 | 
						|
								            request.Cookies = ParseCookies(headers) ?? new CookieCollection();
							 | 
						|
								
							 | 
						|
								            // 匹配 API。
							 | 
						|
								            var application = null as string;
							 | 
						|
								            var function = null as string;
							 | 
						|
								            var random = null as string;
							 | 
						|
								            var ticket = null as string;
							 | 
						|
								            var session = null as string;
							 | 
						|
								            var page = null as string;
							 | 
						|
								
							 | 
						|
								            // 解析 POST 请求。
							 | 
						|
								            switch (request.Method)
							 | 
						|
								            {
							 | 
						|
								                case HttpMethod.PATCH:
							 | 
						|
								                case HttpMethod.POST:
							 | 
						|
								                case HttpMethod.PUT:
							 | 
						|
								                    var preRead = provider.PreRead();
							 | 
						|
								                    if (string.IsNullOrEmpty(preRead))
							 | 
						|
								                    {
							 | 
						|
								                        var post = null as byte[];
							 | 
						|
								                        var length = 0L;
							 | 
						|
								                        var max = options.MaxRequestBody;
							 | 
						|
								                        if (max == 0) post = new byte[0];
							 | 
						|
								                        else if (max < 0) post = provider.RequestBody().Read();
							 | 
						|
								                        else
							 | 
						|
								                        {
							 | 
						|
								                            length = provider.GetContentLength();
							 | 
						|
								                            if (length <= max) post = provider.RequestBody().Read();
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        length = post == null ? 0 : post.Length;
							 | 
						|
								                        if (length > 1)
							 | 
						|
								                        {
							 | 
						|
								                            request.PostData = post;
							 | 
						|
								                            if (length < 104857600)
							 | 
						|
								                            {
							 | 
						|
								                                var text = TextUtility.FromBytes(post);
							 | 
						|
								                                request.PostText = text;
							 | 
						|
								
							 | 
						|
								                                // 尝试解析 Json,首尾必须是“{}”或“[]”。
							 | 
						|
								                                var first = post[0];
							 | 
						|
								                                var last = post[length - 1];
							 | 
						|
								                                if ((first == 123 && last == 125) || (first == 91 && last == 93))
							 | 
						|
								                                {
							 | 
						|
								                                    var json = Json.From(text);
							 | 
						|
								                                    if (json != null && json.IsObject)
							 | 
						|
								                                    {
							 | 
						|
								                                        application = json["application"];
							 | 
						|
								                                        function = json["function"];
							 | 
						|
								                                        random = json["random"];
							 | 
						|
								                                        ticket = json["ticket"];
							 | 
						|
								                                        session = json["session"];
							 | 
						|
								                                        page = json["page"];
							 | 
						|
								
							 | 
						|
								                                        var data = json.GetProperty("data");
							 | 
						|
								                                        request.PostJson = json;
							 | 
						|
								                                        request.Data = data ?? Json.NewObject();
							 | 
						|
								                                    }
							 | 
						|
								                                }
							 | 
						|
								
							 | 
						|
								                                // 尝试解析 Form,需要 application/x-www-form-urlencoded
							 | 
						|
								                                var contentType = headers.GetValue("Content-Type") ?? "";
							 | 
						|
								                                if (contentType.Contains("urlencoded")) request.Form = ApiUtility.Parameters(text);
							 | 
						|
								                            }
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    break;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 解析 URL 参数。
							 | 
						|
								            // URL 参数的优先级应高于 URL 路径,以避免反向代理产生的路径问题。
							 | 
						|
								            var urlParameters = ApiUtility.Parameters(request.Url.Query);
							 | 
						|
								            if (string.IsNullOrEmpty(application)) application = urlParameters.GetValue("application");
							 | 
						|
								            if (string.IsNullOrEmpty(function)) function = urlParameters.GetValue("function");
							 | 
						|
								            if (string.IsNullOrEmpty(random)) random = urlParameters.GetValue("random");
							 | 
						|
								            if (string.IsNullOrEmpty(ticket)) ticket = urlParameters.GetValue("ticket");
							 | 
						|
								            if (string.IsNullOrEmpty(session)) session = urlParameters.GetValue("session");
							 | 
						|
								            if (string.IsNullOrEmpty(page)) page = urlParameters.GetValue("page");
							 | 
						|
								
							 | 
						|
								            // 从 Cookie 中获取 Ticket。
							 | 
						|
								            var cookies = request.Cookies;
							 | 
						|
								            if (string.IsNullOrEmpty(ticket)) ticket = cookies.GetValue("ticket");
							 | 
						|
								
							 | 
						|
								            // 最后检查 URL 路径。
							 | 
						|
								            var paths = (request.Url.AbsolutePath ?? "").Split('/');
							 | 
						|
								            if (string.IsNullOrEmpty(application) && paths.Length >= 2) application = TextUtility.DecodeUrl(paths[1]);
							 | 
						|
								            if (string.IsNullOrEmpty(function) && paths.Length >= 3) function = TextUtility.DecodeUrl(paths[2]);
							 | 
						|
								
							 | 
						|
								            // 修正内容。
							 | 
						|
								            application = TextUtility.Trim(application);
							 | 
						|
								            function = TextUtility.Trim(function);
							 | 
						|
								            random = TextUtility.Trim(random);
							 | 
						|
								            ticket = TextUtility.Trim(ticket);
							 | 
						|
								            session = TextUtility.Trim(session);
							 | 
						|
								            page = TextUtility.Trim(page);
							 | 
						|
								
							 | 
						|
								            // 设置请求:回传。
							 | 
						|
								            request.Application = application;
							 | 
						|
								            request.Function = function;
							 | 
						|
								            request.Random = random;
							 | 
						|
								
							 | 
						|
								            // 设置请求:不回传。
							 | 
						|
								            request.Ticket = ticket;
							 | 
						|
								            request.Session = session;
							 | 
						|
								            request.Page = page;
							 | 
						|
								
							 | 
						|
								            return request;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        static StringPairs PrepareHeaders(ApiOptions options, ApiResponse response, ApiRequest request = null)
							 | 
						|
								        {
							 | 
						|
								            var merged = new StringPairs();
							 | 
						|
								            if (options != null)
							 | 
						|
								            {
							 | 
						|
								                // 跨域访问。
							 | 
						|
								                if (options.WithAccessControl)
							 | 
						|
								                {
							 | 
						|
								                    merged.Add("Access-Control-Allow-Headers", "Content-Type");
							 | 
						|
								                    merged.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
							 | 
						|
								                    merged.Add("Access-Control-Allow-Origin", "*");
							 | 
						|
								
							 | 
						|
								                    var maxage = options.AccessControlMaxAge;
							 | 
						|
								                    if (maxage > 0) merged.Add("Access-Control-Max-Age", maxage.ToString());
							 | 
						|
								
							 | 
						|
								                    if (request != null && request.Headers != null)
							 | 
						|
								                    {
							 | 
						|
								                        var @private = request.Headers.GetValue("Access-Control-Request-Private-Network");
							 | 
						|
								                        if (NumberUtility.Boolean(@private)) merged.Add("Access-Control-Allow-Private-Network", "true");
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // Content-Type 检查。
							 | 
						|
								                if (options.WithContentTypeOptions)
							 | 
						|
								                {
							 | 
						|
								                    merged.Add("X-Content-Type-Options", "nosniff");
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 用于客户端,当前页面使用 HTTPS 时,将资源升级为 HTTPS。
							 | 
						|
								                if (options.UpgradeHttps)
							 | 
						|
								                {
							 | 
						|
								                    merged.Add("Content-Security-Policy", "upgrade-insecure-requests");
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 包含 API 的处理时间。
							 | 
						|
								                if (options.WithDuration && response != null)
							 | 
						|
								                {
							 | 
						|
								                    if (response.Duration.NotEmpty()) merged.Add("Duration", response.Duration);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            if (response != null)
							 | 
						|
								            {
							 | 
						|
								                // Cookies。
							 | 
						|
								                var setCookies = SetCookie(response.Cookies);
							 | 
						|
								                if (setCookies != null)
							 | 
						|
								                {
							 | 
						|
								                    foreach (var value in setCookies) merged.Add("Set-Cookie", value);
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                // 自定义头。
							 | 
						|
								                var headers = response.Headers;
							 | 
						|
								                if (headers != null)
							 | 
						|
								                {
							 | 
						|
								                    foreach (var header in headers)
							 | 
						|
								                    {
							 | 
						|
								                        var key = TextUtility.Trim(header.Name);
							 | 
						|
								                        if (string.IsNullOrEmpty(key)) continue;
							 | 
						|
								                        var value = header.Value;
							 | 
						|
								                        if (string.IsNullOrEmpty(value)) continue;
							 | 
						|
								                        merged.Add(key, value);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            return merged;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, string type, byte[] bytes)
							 | 
						|
								        {
							 | 
						|
								            var preWrite = provider.PreWrite();
							 | 
						|
								            if (!string.IsNullOrEmpty(preWrite)) return;
							 | 
						|
								
							 | 
						|
								            if (response != null)
							 | 
						|
								            {
							 | 
						|
								                var responsePreOutput = response.PreOutput;
							 | 
						|
								                if (responsePreOutput != null)
							 | 
						|
								                {
							 | 
						|
								                    var @continue = responsePreOutput.Invoke(_context);
							 | 
						|
								                    if (!@continue) return;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var invokerPreOutput = _context.Invoker.PreOutput;
							 | 
						|
								            if (invokerPreOutput != null)
							 | 
						|
								            {
							 | 
						|
								                var @continue = invokerPreOutput.Invoke(_context);
							 | 
						|
								                if (!@continue) return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var optionsPreOutput = _context.Options.PreOutput;
							 | 
						|
								            if (optionsPreOutput != null)
							 | 
						|
								            {
							 | 
						|
								                var @continue = optionsPreOutput.Invoke(_context);
							 | 
						|
								                if (!@continue) return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var headers = PrepareHeaders(options, response);
							 | 
						|
								            foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
							 | 
						|
								
							 | 
						|
								            provider.SetCache(0);
							 | 
						|
								            provider.SetContentType(string.IsNullOrEmpty(type) ? "application/octet-stream" : type);
							 | 
						|
								
							 | 
						|
								            var length = bytes == null ? 0 : bytes.Length;
							 | 
						|
								            provider.SetContentLength(length);
							 | 
						|
								            if (length > 0) provider.ResponseBody().Write(bytes, 0, bytes.Length);
							 | 
						|
								            provider.Sent();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, ApiRequest request, HttpMethod method)
							 | 
						|
								        {
							 | 
						|
								            var preWrite = provider.PreWrite();
							 | 
						|
								            if (!string.IsNullOrEmpty(preWrite)) return;
							 | 
						|
								
							 | 
						|
								            if (response != null)
							 | 
						|
								            {
							 | 
						|
								                var responsePreOutput = response.PreOutput;
							 | 
						|
								                if (responsePreOutput != null)
							 | 
						|
								                {
							 | 
						|
								                    var @continue = responsePreOutput.Invoke(_context);
							 | 
						|
								                    if (!@continue) return;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var invokerPreOutput = _context.Invoker.PreOutput;
							 | 
						|
								            if (invokerPreOutput != null)
							 | 
						|
								            {
							 | 
						|
								                var @continue = invokerPreOutput.Invoke(_context);
							 | 
						|
								                if (!@continue) return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var optionsPreOutput = _context.Options.PreOutput;
							 | 
						|
								            if (optionsPreOutput != null)
							 | 
						|
								            {
							 | 
						|
								                var @continue = optionsPreOutput.Invoke(_context);
							 | 
						|
								                if (!@continue) return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // 设置头。
							 | 
						|
								            var headers = PrepareHeaders(options, response, request);
							 | 
						|
								            foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
							 | 
						|
								
							 | 
						|
								            // 自定义模型
							 | 
						|
								            var model = response.Model as IApiModel;
							 | 
						|
								            var result = response.Model as IActionResult;
							 | 
						|
								            if (model != null)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    model.Output(_context);
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex)
							 | 
						|
								                {
							 | 
						|
								                    Logger.Internals.Exception(ex, model);
							 | 
						|
								                }
							 | 
						|
								                RuntimeUtility.Dispose(model);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								            else if (result != null)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    result.ExecuteResult(_context);
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex)
							 | 
						|
								                {
							 | 
						|
								                    Logger.Internals.Exception(ex, result);
							 | 
						|
								                }
							 | 
						|
								                RuntimeUtility.Dispose(result);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            var text = ApiUtility.ToJson(response, options);
							 | 
						|
								            var bytes = TextUtility.Bytes(text);
							 | 
						|
								            provider.SetCache(0);
							 | 
						|
								            provider.SetContentType("text/json; charset=utf-8");
							 | 
						|
								            provider.SetContentLength(bytes.Length);
							 | 
						|
								            var stream = provider.ResponseBody();
							 | 
						|
								            if (stream != null && stream.CanWrite) stream.Write(bytes, 0, bytes.Length);
							 | 
						|
								            provider.Sent();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 |