diff --git a/Apewer.Web/AspNetBridge/BridgeController.cs b/Apewer.Web/AspNetBridge/BridgeController.cs
index 47ffa21..5370bb8 100644
--- a/Apewer.Web/AspNetBridge/BridgeController.cs
+++ b/Apewer.Web/AspNetBridge/BridgeController.cs
@@ -157,6 +157,7 @@ namespace Apewer.AspNetBridge
             }
 
             // 准备参数。
+            var pis = CollectionUtility.Vacuum(route.Parameters.Map(x => ApiParameter.Parse(x)));
             var ps = ReadParameters(Request, route.Parameters);
 
             // 准备控制器。
diff --git a/Apewer.Web/Web/AspNetCoreProvider.cs b/Apewer.Web/Web/AspNetCoreProvider.cs
index 99157b7..ae77ab8 100644
--- a/Apewer.Web/Web/AspNetCoreProvider.cs
+++ b/Apewer.Web/Web/AspNetCoreProvider.cs
@@ -1,5 +1,6 @@
 #if NETCORE
 
+using Apewer.Network;
 using Microsoft.AspNetCore.Http;
 using System;
 using System.Collections.Generic;
@@ -10,13 +11,16 @@ namespace Apewer.Web
 {
 
     /// <summary>用于网站的服务程序。</summary>
-    public class AspNetCoreProvider : ApiProvider
+    public class AspNetCoreProvider : ApiProvider<HttpContext>
     {
 
         private HttpContext context;
         private HttpRequest request;
         private HttpResponse response;
 
+        /// <summary>HttpContext</summary>
+        public override HttpContext Context { get => context; }
+
         /// <summary>创建服务程序实例。</summary>
         /// <exception cref="ArgumentNullException"></exception>
         public AspNetCoreProvider(HttpContext context)
@@ -39,7 +43,7 @@ namespace Apewer.Web
         public override Uri GetUrl()
         {
             var https = request.IsHttps;
-            var port = context.Connection.LocalPort;
+            var port = Context.Connection.LocalPort;
             var query = request.QueryString == null ? null : request.QueryString.Value;
 
             var sb = new StringBuilder();
@@ -66,11 +70,11 @@ namespace Apewer.Web
         public override string GetReferrer() => null;
 
         /// <summary>获取请求的头。</summary>
-        public override StringPairs GetHeaders()
+        public override HttpHeaders GetHeaders()
         {
             var headers = request.Headers;
-            var sp = new StringPairs();
-            if (headers == null) return sp;
+            var result = new HttpHeaders();
+            if (headers == null) return result;
             foreach (var key in headers.Keys)
             {
                 if (string.IsNullOrEmpty(key)) continue;
@@ -78,11 +82,11 @@ namespace Apewer.Web
                 {
                     var value = headers[key];
                     if (string.IsNullOrEmpty(value)) continue;
-                    sp.Add(key, value);
+                    result.Add(key, value);
                 }
                 catch { }
             }
-            return sp;
+            return result;
         }
 
         /// <summary>获取请求的内容类型。</summary>
diff --git a/Apewer.Windows/Internals/Interop/Constant.cs b/Apewer.Windows/Internals/Interop/Constant.cs
index 476298e..8875856 100644
--- a/Apewer.Windows/Internals/Interop/Constant.cs
+++ b/Apewer.Windows/Internals/Interop/Constant.cs
@@ -89,6 +89,10 @@ namespace Apewer.Internals.Interop
         /// <summary></summary>
         public const int PROCESS_QUERY_INFORMATION = 0x400;
 
+        public const int PROCESS_VM_READ = 0x0010;
+
+        public const int PROCESS_VM_WRITE = 0x0020;
+
         /// <summary></summary>
         public const int SC_MOVE = 0xF010;
 
diff --git a/Apewer.Windows/Internals/Interop/Kernel32.cs b/Apewer.Windows/Internals/Interop/Kernel32.cs
index 3b2ad7b..ea776b6 100644
--- a/Apewer.Windows/Internals/Interop/Kernel32.cs
+++ b/Apewer.Windows/Internals/Interop/Kernel32.cs
@@ -13,7 +13,7 @@ namespace Apewer.Internals.Interop
         public static extern int CloseHandle(int hObject);
 
         [DllImport("kernel32.dll", SetLastError = true)]
-        [return: MarshalAs(UnmanagedType.Bool)]
+        // [return: MarshalAs(UnmanagedType.Bool)]
         public static extern bool CloseHandle(IntPtr hObject);
 
         [DllImport("kernel32.dll")]
@@ -64,11 +64,12 @@ namespace Apewer.Internals.Interop
         [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
         public static extern int OpenProcess(int dwDesiredAccess, int bInheritHandle, int dwProcessId);
 
-        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
-        public static extern int OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+        [DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
+        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
 
-        [DllImport("kernel32.dll")]
-        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, IntPtr lpNumberOfBytesRead);
+        // BOOL ReadProcessMemory([in] HANDLE hProcess, [in] LPCVOID lpBaseAddress, [out] LPVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesRead);
+        [DllImport("kernel32.dll ")]
+        public static extern bool ReadProcessMemory(IntPtr hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesRead);
 
         /// <summary>Copies the contents of a source memory block to a destination memory block, and supports overlapping source and destination memory blocks.</summary>
         /// <param name="Destination">A pointer to the destination memory block to copy the bytes to.</param>
@@ -84,8 +85,9 @@ namespace Apewer.Internals.Interop
         [DllImport("kernel32.dll")]
         public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
 
-        [DllImportAttribute("kernel32.dll")]
-        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, int[] lpBuffer, int nSize, IntPtr lpNumberOfBytesWritten);
+        // BOOL WriteProcessMemory([in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten);
+        [DllImport("kernel32.dll")]
+        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
 
     }
 
diff --git a/Apewer.Windows/WindowsUtility.cs b/Apewer.Windows/WindowsUtility.cs
index 52833e2..b0bf8bf 100644
--- a/Apewer.Windows/WindowsUtility.cs
+++ b/Apewer.Windows/WindowsUtility.cs
@@ -25,9 +25,16 @@ namespace Apewer
 {
 
     /// <summary>Windows 实用工具。</summary>
-    public class WindowsUtility
+    public static class WindowsUtility
     {
 
+        #region 句柄
+
+        /// <summary>关闭句柄。</summary>
+        public static bool CloseHandle(IntPtr handle) => Kernel32.CloseHandle(handle);
+
+        #endregion
+
         #region 进程。
 
 #if NETFX
@@ -238,45 +245,143 @@ namespace Apewer
             return 0;
         }
 
-        /// <summary>读取内存中的值。</summary>
-        /// <param name="pid">进程 ID。</param>
-        /// <param name="address">地址。</param>
-        /// <param name="throw">抛出发生的异常。</param>
-        public static int ReadMemoryInt32(int address, int pid, bool @throw = true)
+        /// <summary>打开现有的本地进程对象。</summary>
+        /// <param name="processId">要打开的本地进程的标识符。</param>
+        /// <returns>指定进程的打开句柄。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="SystemException" />
+        public static IntPtr OpenProcess(int processId)
+        {
+            if (processId == 0) throw new ArgumentNullException(nameof(processId));
+            var handle = Kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, false, processId);
+            if (handle == IntPtr.Zero) throw new SystemException($"打开进程 {processId} 失败。");
+            return handle;
+        }
+
+        /// <summary>打开现有的本地进程对象。</summary>
+        /// <param name="processId">要打开的本地进程的标识符。</param>
+        /// <param name="callback">使用句柄。</param>
+        /// <returns>指定进程的打开句柄。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="SystemException" />
+        public static void OpenProcess(int processId, Action<IntPtr> callback)
         {
+            if (processId == 0) throw new ArgumentNullException(nameof(processId));
+            if (callback == null) throw new ArgumentNullException(nameof(callback));
+
+            var handle = IntPtr.Zero;
             try
             {
-                var buffer = new byte[4];
-                var pinned = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
-                var process = new IntPtr(OpenProcess(0x1F0FFF, false, pid));
-                ReadProcessMemory(process, (IntPtr)address, pinned, 4, IntPtr.Zero);
-                CloseHandle(process);
-                return Marshal.ReadInt32(pinned);
+                handle = OpenProcess(processId);
+                callback.Invoke(handle);
             }
-            catch (Exception ex)
+            finally
             {
-                if (@throw) throw ex;
-                return 0;
+                if (handle != IntPtr.Zero) CloseHandle(handle);
             }
         }
 
-        /// <summary>将值写入指定内存地址中。</summary>
-        /// <param name="address">地址。</param>
-        /// <param name="pid">进程 ID。</param>
-        /// <param name="value">Int32 值。</param>
-        /// <param name="throw">抛出发生的异常。</param>
-        public static void WriteMemoryInt32(int address, int pid, int value, bool @throw = true)
+        /// <summary>打开现有的本地进程对象。</summary>
+        /// <param name="processId">要打开的本地进程的标识符。</param>
+        /// <param name="callback">使用句柄。</param>
+        /// <returns>指定进程的打开句柄。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="SystemException" />
+        public static T OpenProcess<T>(int processId, Func<IntPtr, T> callback)
         {
+            if (processId == 0) throw new ArgumentNullException(nameof(processId));
+            if (callback == null) throw new ArgumentNullException(nameof(callback));
+
+            var handle = IntPtr.Zero;
             try
             {
-                var hProcess = OpenProcess(0x1F0FFF, false, pid); // 0x1F0FFF 最高权限
-                WriteProcessMemory(new IntPtr(hProcess), (IntPtr)address, new[] { value }, 4, IntPtr.Zero);
-                CloseHandle(hProcess);
+                handle = OpenProcess(processId);
+                return callback.Invoke(handle);
             }
-            catch (Exception ex)
+            finally
+            {
+                if (handle != IntPtr.Zero) CloseHandle(handle);
+            }
+        }
+
+        /// <summary>读取指定进程的内存。</summary>
+        /// <param name="process">进程。</param>
+        /// <param name="address">要读取的内存地址。</param>
+        /// <param name="length">要读取的字节数。</param>
+        /// <returns>读取到的数据。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="ArgumentOutOfRangeException" />
+        /// <exception cref="SystemException" />
+        public static byte[] ReadMemory(this Process process, IntPtr address, int length)
+        {
+            if (process == null) throw new ArgumentNullException(nameof(process));
+            if (address == IntPtr.Zero) throw new ArgumentNullException(nameof(address));
+            if (length < 1) throw new ArgumentNullException(nameof(length));
+
+            return OpenProcess(process.Id, (processHandle) => ReadMemory(processHandle, address, length));
+        }
+
+        /// <summary>读取指定进程的内存。</summary>
+        /// <param name="processHandle">进程句柄。</param>
+        /// <param name="address">要读取的内存地址。</param>
+        /// <param name="length">要读取的字节数。</param>
+        /// <returns>读取到的数据。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="ArgumentOutOfRangeException" />
+        /// <exception cref="SystemException" />
+        public static byte[] ReadMemory(IntPtr processHandle, IntPtr address, int length)
+        {
+            if (processHandle == IntPtr.Zero) throw new ArgumentNullException(nameof(processHandle));
+            if (address == IntPtr.Zero) throw new ArgumentNullException(nameof(address));
+            if (length < 1) throw new ArgumentOutOfRangeException(nameof(length)); ;
+
+            var buffer = new byte[length];
+            if (ReadProcessMemory(processHandle, address.ToInt32(), buffer, length, out var read))
             {
-                if (@throw) throw ex;
+                if (read == length) return buffer;
+
+                // 收缩数组
+                var array = new byte[read];
+                if (read > 0) Buffer.BlockCopy(buffer, 0, array, 0, read);
+                return array;
             }
+
+            throw new SystemException($"从内存地址读取数据失败。");
+        }
+
+        /// <summary>在进程的指定地址写入数据。</summary>
+        /// <param name="process">进程。</param>
+        /// <param name="address">要读取的内存地址。</param>
+        /// <param name="data">要写入的数据。</param>
+        /// <returns>写入的字节数。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="SystemException" />
+        public static int WriteMemory(this Process process, IntPtr address, byte[] data)
+        {
+            if (process == null) throw new ArgumentNullException(nameof(process));
+            if (address == IntPtr.Zero) throw new ArgumentNullException(nameof(address));
+            if (data == null) throw new ArgumentNullException(nameof(data));
+            if (data.Length < 1) return 0;
+
+            return OpenProcess(process.Id, processHandle => WriteMemory(processHandle, address, data));
+        }
+
+        /// <summary>在进程的指定地址写入数据。</summary>
+        /// <param name="processHandle">进程句柄。</param>
+        /// <param name="address">要读取的内存地址。</param>
+        /// <param name="data">要写入的数据。</param>
+        /// <returns>写入的字节数。</returns>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="SystemException" />
+        public static int WriteMemory(IntPtr processHandle, IntPtr address, byte[] data)
+        {
+            if (processHandle == IntPtr.Zero) throw new ArgumentNullException(nameof(processHandle));
+            if (address == IntPtr.Zero) throw new ArgumentNullException(nameof(address));
+            if (data == null) throw new ArgumentNullException(nameof(data));
+            if (data.Length < 1) return 0;
+
+            if (WriteProcessMemory(processHandle, address, data, data.Length, out var written)) return written;
+            throw new SystemException($"向内存地址写入数据失败。");
         }
 
         #endregion
diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props
index 67efd36..f805432 100644
--- a/Apewer/Apewer.props
+++ b/Apewer/Apewer.props
@@ -9,7 +9,7 @@
         <Description></Description>
         <RootNamespace>Apewer</RootNamespace>
         <Product>Apewer Libraries</Product>
-        <Version>6.7.6</Version>
+        <Version>6.8.0</Version>
     </PropertyGroup>
 
     <!-- 生成 -->
diff --git a/Apewer/Class.cs b/Apewer/Class.cs
index b3ea296..7e29505 100644
--- a/Apewer/Class.cs
+++ b/Apewer/Class.cs
@@ -4,73 +4,88 @@ namespace Apewer
 {
 
     /// <summary>装箱类。</summary>
-    public sealed class Class<T> : IComparable, IComparable<T>, IComparable<Class<T>>
+    public sealed class Class<T> // : IComparable, IComparable<T>, IComparable<Class<T>>
     {
 
-        private bool _hashcode = false;
-        private bool _equals = false;
-
-        /// <summary>装箱对象。</summary>
+        /// <summary>值。</summary>
         public T Value { get; set; }
 
-        /// <summary>创建默认值。</summary>
-        public Class(T value = default, bool tryEquals = true, bool tryHashCode = true)
-        {
-            Value = value;
-            _hashcode = tryHashCode;
-            _equals = tryEquals;
-        }
+        /// <summary>创建装箱实例,值为默认值。</summary>
+        public Class() { }
+
+        /// <summary>创建装箱实例,值为指定值。</summary>
+        public Class(T value) => Value = value;
 
         #region Override
 
         /// <summary></summary>
         public override int GetHashCode()
         {
-            if (_hashcode && Value != null)
-            {
-                return Value.GetHashCode();
-            }
+            if (Value != null) return Value.GetHashCode();
             return base.GetHashCode();
         }
 
         /// <summary></summary>
         public override bool Equals(object obj)
         {
-            if (_equals)
+            if (ReferenceEquals(this, obj)) return true;
+
+            var right = obj as Class<T>;
+            if (Value.IsNull())
+            {
+                if (right == null) return true;
+                if (right.Value.IsNull()) return true;
+                return false;
+            }
+            else
             {
-                var right = obj as Class<T>;
                 if (right == null) return false;
-
-                if (Value == null && right.Value == null) return true;
-                if (Value == null && right.Value != null) return false;
-                if (Value != null && right.Value == null) return false;
+                if (right.Value.IsNull()) return false;
+                if (ReferenceEquals(Value, right.Value)) return true;
                 return Value.Equals(right.Value);
             }
-            return base.Equals(obj);
         }
 
         /// <summary></summary>
         public override string ToString()
         {
-            if (Value == null) return "";
+            if (Value == null) return null;
             return Value.ToString();
         }
 
         #endregion
 
-        #region IComparable
+#if ClassCompare
+
+        #region Compare
 
         /// <summary></summary>
-        /// <exception cref="MissingMemberException"></exception>
         /// <exception cref="NotSupportedException"></exception>
         public int CompareTo(object obj)
         {
-            if (obj != null && obj is T) return CompareTo((T)obj);
-            if (obj != null && obj is Class<T>) return CompareTo(obj as Class<T>);
+            if (typeof(IComparable).IsAssignableFrom(typeof(T)))
+            {
+                var comparable = Value as IComparable;
+                if (comparable.IsNull())
+                {
+                    if (obj is Class<T> bro)
+                    {
+                        if (bro == null) return 0;
+                        if (bro.Value.IsNull()) return 0;
+                        return -1;
+                    }
+
+                    if (obj.IsNull()) return 0;
+                    return -1;
+                }
+                else
+                {
+                    if (obj != null && obj is Class<T> bro) return comparable.CompareTo(bro.Value);
+                    return comparable.CompareTo(obj);
+                }
+            }
 
-            if (Value == null) throw new MissingMemberException(typeof(T).FullName, nameof(Value));
-            if (!(Value is IComparable)) throw new NotSupportedException();
-            return ((IComparable)Value).CompareTo(obj);
+            throw new NotImplementedException($"类型 {typeof(T).Name} 没有实现 {nameof(IComparable)} 接口。");
         }
 
         /// <summary></summary>
@@ -98,6 +113,8 @@ namespace Apewer
 
         #endregion
 
+#endif
+
         #region 运算符。
 
         /// <summary>从 <see cref="Class{T}"/> 到 Boolean 的隐式转换,判断 <see cref="Class{T}"/> 包含值。</summary>
@@ -112,7 +129,7 @@ namespace Apewer
             var text = instance as Class<string>;
             if (text != null) return !string.IsNullOrEmpty(text.Value);
 
-            return instance.NotNull();
+            return instance != null;
         }
 
         /// <summary>从 <see cref="Class{T}"/> 到 T 的隐式转换。</summary>
diff --git a/Apewer/ClockUtility.cs b/Apewer/ClockUtility.cs
index e4fb138..a3b4a4a 100644
--- a/Apewer/ClockUtility.cs
+++ b/Apewer/ClockUtility.cs
@@ -1,7 +1,4 @@
-using Apewer.Internals;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
+using System;
 using System.Globalization;
 using System.Text;
 
@@ -22,11 +19,10 @@ namespace Apewer
             if (value is DateTime dt) return dt;
             if (value.IsNull()) return null;
 
-            DateTime result;
             try
             {
                 var text = value.ToString();
-                var parsed = System.DateTime.TryParse(text, out result);
+                var parsed = System.DateTime.TryParse(text, out var result);
                 return parsed ? new Class<DateTime>(result) : null;
             }
             catch
@@ -46,9 +42,6 @@ namespace Apewer
         /// <summary>创建新的零值 DateTime 对象。</summary>
         public static DateTime Zero { get => _zero; }
 
-        /// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为本地时间。</summary>
-        public static DateTime Origin { get => _origin; }
-
         /// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为协调通用时间 (UTC)。</summary>
         public static DateTime UtcOrigin { get => _utc_origin; }
 
@@ -76,6 +69,26 @@ namespace Apewer
 
         #endregion
 
+        #region Round
+
+        /// <summary>对齐时间,小于 <see cref="DateTimePart"/> 的部分将被舍弃。</summary>
+        public static DateTime Round(this DateTime dateTime, DateTimePart datePart)
+        {
+            switch (datePart)
+            {
+                case DateTimePart.Year: return new DateTime(dateTime.Year, 1, 1, 0, 0, 0, 0, dateTime.Kind);
+                case DateTimePart.Month: return new DateTime(dateTime.Year, dateTime.Month, 1, 0, 0, 0, 0, dateTime.Kind);
+                case DateTimePart.Day: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0, dateTime.Kind);
+                case DateTimePart.Hour: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, 0, dateTime.Kind);
+                case DateTimePart.Minute: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0, 0, dateTime.Kind);
+                case DateTimePart.Second: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 0, dateTime.Kind);
+                case DateTimePart.Millisecond: return dateTime;
+                default: throw new ArgumentException($"指定的 {nameof(DateTimePart)} 不受支持。");
+            }
+        }
+
+        #endregion
+
         #region Common
 
         /// <summary>判断指定年份是闰年。</summary>
@@ -88,9 +101,10 @@ namespace Apewer
         }
 
         /// <summary>判断指定年份是闰年。</summary>
-        public static bool IsLeapYear(DateTime datetime) => IsLeapYear(SafeDateTime(datetime).Year);
+        public static bool IsLeapYear(DateTime dateTime) => IsLeapYear(dateTime.Year);
 
         /// <summary>获取指定年月的天数。</summary>
+        /// <exception cref="ArgumentOutOfRangeException" />
         public static int MonthDays(int year, int month)
         {
             switch (month)
@@ -98,41 +112,7 @@ namespace Apewer
                 case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31;
                 case 4: case 6: case 9: case 11: return 30;
                 case 2: return IsLeapYear(year) ? 29 : 28;
-                default: return 0;
-            }
-        }
-
-        /// <summary>尝试获取安全的 DateTime 对象。</summary>
-        public static DateTime SafeDateTime(object datetime)
-        {
-            if (datetime is DateTime)
-            {
-                var span = (DateTime)datetime - Origin;
-                return Origin.AddMilliseconds(span.TotalMilliseconds);
-            }
-            else
-            {
-                if (datetime == null) return Zero;
-                var type = datetime.GetType();
-                if (type.Equals(typeof(string)))
-                {
-                    var s = datetime as string;
-                    if (string.IsNullOrEmpty(s)) return Zero;
-                    DateTime value;
-                    var success = TryParse(s, out value);
-                    return success ? value : Zero;
-                }
-                else
-                {
-                    if (datetime == null) return Zero;
-                    try
-                    {
-                        var s = datetime.ToString();
-                        if (string.IsNullOrEmpty(s)) return Zero;
-                        return SafeDateTime(s);
-                    }
-                    catch { return Zero; }
-                }
+                default: throw new ArgumentOutOfRangeException(nameof(month));
             }
         }
 
@@ -140,35 +120,92 @@ namespace Apewer
 
         #region Stamp
 
+        /// <summary>自定义从 DateTime 转为时间戳的方法。</summary>
+        public static Func<DateTime, long> CustomToStamp { get; set; }
+
+        /// <summary>自定义从时间戳转为 DateTime 的方法。</summary>
+        public static Func<long, DateTime> CustomFromStamp { get; set; }
+
         /// <summary>获取当前本地时间的毫秒时间戳。</summary>
-        public static long NowStamp { get => Stamp(Now); }
+        public static long NowStamp { get => ToStamp(Now); }
 
         /// <summary>获取当前 UTC 的毫秒时间戳。</summary>
-        public static long UtcStamp { get => Stamp(UtcNow); }
+        public static long UtcStamp { get => ToStamp(UtcNow); }
 
-        /// <summary>获取毫秒时间戳。</summary>
-        public static long Stamp(DateTime datetime, bool byMilliseconds = true)
+        /// <summary>获取毫秒时间戳。当指定了 <see cref="CustomToStamp"/> 时将优先使用自定义的方法。</summary>
+        /// <remarks>默认不判断参数的时区,与 <see cref="DateTimeKind.Unspecified"/> 相同。</remarks>
+        public static long ToStamp(DateTime dateTime)
         {
-            var span = datetime - Origin;
-            var value = byMilliseconds ? span.TotalMilliseconds : span.TotalSeconds;
-            var stamp = Convert.ToInt64(value);
+            var converter = CustomToStamp;
+            if (converter != null) return converter.Invoke(dateTime);
+
+            var span = dateTime - _origin;
+            var value = span.TotalMilliseconds;
+            var stamp = Convert.ToInt64(Math.Floor(value));
             return stamp;
         }
 
-        /// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
+        /// <summary>获取 UTC 毫秒时间戳</summary>
+        public static long ToUtcStamp(DateTime dateTime)
+        {
+            if (dateTime.Kind == DateTimeKind.Local) dateTime = dateTime.ToUniversalTime();
+
+            var span = dateTime - _utc_origin;
+            var value = span.TotalMilliseconds;
+            var stamp = Convert.ToInt64(Math.Floor(value));
+            return stamp;
+        }
+
+        /// <summary>解析毫秒时间戳,获取 DateTime 对象。当指定了 <see cref="CustomFromStamp"/> 时将优先使用自定义的方法。</summary>
+        /// <remarks>默认不判断系统时区,返回的结果是 <see cref="DateTimeKind.Unspecified"/>。</remarks>
         /// <exception cref="ArgumentOutOfRangeException"></exception>
-        public static DateTime FromStamp(long stamp, DateTimeKind kind = DateTimeKind.Unspecified, bool throwException = true)
+        public static DateTime FromStamp(long stamp)
         {
-            try
-            {
-                var origin = NewOrigin(kind);
-                var datetime = origin.AddMilliseconds(Convert.ToDouble(stamp));
-                return datetime;
-            }
-            catch
+            var converter = CustomFromStamp;
+            if (converter != null) return converter.Invoke(stamp);
+            return FromStamp(stamp, DateTimeKind.Unspecified, DateTimeKind.Unspecified);
+        }
+
+        /// <summary>从毫秒时间戳获取 DateTime 对象。</summary>
+        /// <exception cref="ArgumentOutOfRangeException"></exception>
+        public static DateTime FromStamp(long stamp, DateTimeKind stampKind, DateTimeKind dateTimeKind)
+        {
+            switch (dateTimeKind)
             {
-                if (throwException) throw new ArgumentOutOfRangeException();
-                return Origin;
+                case DateTimeKind.Unspecified:
+                    switch (stampKind)
+                    {
+                        case DateTimeKind.Unspecified:
+                        case DateTimeKind.Utc:
+                        case DateTimeKind.Local:
+                            return _origin.AddMilliseconds(stamp);
+                        default:
+                            throw new ArgumentOutOfRangeException(nameof(stampKind));
+                    }
+                case DateTimeKind.Utc:
+                    switch (stampKind)
+                    {
+                        case DateTimeKind.Unspecified:
+                        case DateTimeKind.Utc:
+                            return _utc_origin.AddMilliseconds(stamp);
+                        case DateTimeKind.Local:
+                            return NewOrigin(DateTimeKind.Local).AddMilliseconds(stamp).ToUniversalTime();
+                        default:
+                            throw new ArgumentOutOfRangeException(nameof(stampKind));
+                    }
+                case DateTimeKind.Local:
+                    switch (stampKind)
+                    {
+                        case DateTimeKind.Unspecified:
+                        case DateTimeKind.Utc:
+                            return _utc_origin.AddMilliseconds(stamp).ToLocalTime();
+                        case DateTimeKind.Local:
+                            return NewOrigin(DateTimeKind.Local).AddMilliseconds(stamp);
+                        default:
+                            throw new ArgumentOutOfRangeException(nameof(stampKind));
+                    }
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(dateTimeKind));
             }
         }
 
@@ -229,29 +266,27 @@ namespace Apewer
         public static string CompactDate { get { return Compact(Now, true, false, false, false); } }
 
         /// <summary>转换 DateTime 对象到易于阅读的文本。</summary>
-        public static string Lucid(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
+        public static string Lucid(DateTime dateTime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
         {
-            var safe = SafeDateTime(datetime);
             var sb = new StringBuilder();
-            if (date) sb.Append(FormatDate(safe, true));
+            if (date) sb.Append(FormatDate(dateTime, true));
             if (time)
             {
                 if (date) sb.Append(" ");
-                sb.Append(FormatTime(safe, true, seconds, milliseconds));
+                sb.Append(FormatTime(dateTime, true, seconds, milliseconds));
             }
             var lucid = sb.ToString();
             return lucid;
         }
 
         /// <summary>转换 DateTime 对象到紧凑的文本。</summary>
-        public static string Compact(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
+        public static string Compact(DateTime dateTime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
         {
-            var safe = SafeDateTime(datetime);
             var sb = new StringBuilder();
-            if (date) sb.Append(FormatDate(safe, false));
+            if (date) sb.Append(FormatDate(dateTime, false));
             if (time)
             {
-                sb.Append(FormatTime(safe, false, seconds, milliseconds));
+                sb.Append(FormatTime(dateTime, false, seconds, milliseconds));
             }
             var lucid = sb.ToString();
             return lucid;
diff --git a/Apewer/CollectionUtility.cs b/Apewer/CollectionUtility.cs
index 5d5ec57..4daff30 100644
--- a/Apewer/CollectionUtility.cs
+++ b/Apewer/CollectionUtility.cs
@@ -311,7 +311,7 @@ namespace Apewer
         /// <summary>对元素去重,且去除 NULL 值。</summary>
         public static T[] Distinct<T>(IEnumerable<T> items)
         {
-            if (items == null) throw new ArgumentNullException(nameof(items));
+            if (items != null) throw new ArgumentNullException(nameof(items));
             var count = Count(items);
             var added = 0;
             var array = new T[count];
@@ -346,6 +346,9 @@ namespace Apewer
             return array;
         }
 
+        /// <summary>清理集合,去除 NULL 值。</summary>
+        public static T[] Vacuum<T>(this IEnumerable<T> items) => FindAll(items, x => x != null);
+
         /// <summary>获取可枚举集合的部分元素。</summary>
         /// <typeparam name="T">集合元素的类型。</typeparam>
         /// <param name="objects">原集合。</param>
@@ -650,10 +653,68 @@ namespace Apewer
         #region Find
 
         /// <summary>根据条件筛选,将符合条件的元素组成新数组。</summary>
-        public static T[] FindAll<T>(this T[] array, Predicate<T> match) => System.Array.FindAll<T>(array, match);
+        public static T[] FindAll<T>(this IEnumerable<T> collection, Predicate<T> match)
+        {
+            if (collection == null) new ArgumentNullException(nameof(collection));
+            if (match == null) throw new ArgumentNullException(nameof(match));
+
+            if (collection is T[] array)
+            {
+                return System.Array.FindAll<T>(array, match);
+            }
+            else if (collection is IList<T> list)
+            {
+                var count = list.Count;
+                var result = new List<T>(count);
+                for (var i = 0; i < count; i++)
+                {
+                    var matched = match.Invoke(list[i]);
+                    if (matched) result.Add(list[i]);
+                }
+                return result.ToArray();
+            }
+            else
+            {
+                var result = new List<T>();
+                foreach (var item in collection)
+                {
+                    var matched = match.Invoke(item);
+                    if (matched) result.Add(item);
+                }
+                return result.ToArray();
+            }
+        }
 
         /// <summary>根据条件筛选,找到第一个符合条件的元素。</summary>
-        public static T Find<T>(this T[] array, Predicate<T> match) => System.Array.Find<T>(array, match);
+        public static T Find<T>(this IEnumerable<T> collection, Predicate<T> match)
+        {
+            if (collection == null) throw new ArgumentNullException(nameof(collection));
+            if (match == null) throw new ArgumentNullException(nameof(match));
+
+            if (collection is T[] array)
+            {
+                return System.Array.Find<T>(array, match);
+            }
+            else if (collection is IList<T> list)
+            {
+                var count = list.Count;
+                for (var i = 0; i < count; i++)
+                {
+                    var matched = match.Invoke(list[i]);
+                    if (matched) return list[i];
+                }
+            }
+            else
+            {
+                foreach (var item in collection)
+                {
+                    var matched = match.Invoke(item);
+                    if (matched) return item;
+                }
+            }
+
+            return default;
+        }
 
         #endregion
 
diff --git a/Apewer/DateTimePart.cs b/Apewer/DateTimePart.cs
new file mode 100644
index 0000000..0239ad7
--- /dev/null
+++ b/Apewer/DateTimePart.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer
+{
+
+    /// <summary>表示 DateTime 的部分。</summary>
+    public enum DateTimePart
+    {
+
+        /// <summary></summary>
+        Year,
+
+        /// <summary></summary>
+        Month,
+
+        /// <summary></summary>
+        Day,
+
+        /// <summary></summary>
+        Hour,
+
+        /// <summary></summary>
+        Minute,
+
+        /// <summary></summary>
+        Second,
+
+        /// <summary></summary>
+        Millisecond
+
+    }
+
+}
diff --git a/Apewer/Json.cs b/Apewer/Json.cs
index ab66893..59299c9 100644
--- a/Apewer/Json.cs
+++ b/Apewer/Json.cs
@@ -1,6 +1,4 @@
-using Apewer;
-using Apewer.Internals;
-using Newtonsoft.Json;
+using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using System;
 using System.Collections;
@@ -11,8 +9,6 @@ using System.Dynamic;
 using System.IO;
 #endif
 using System.Reflection;
-using System.Runtime.Serialization;
-using System.Text;
 using static Apewer.NumberUtility;
 using static Apewer.RuntimeUtility;
 
@@ -1739,35 +1735,80 @@ namespace Apewer
             if (json._jtoken == null) return null;
             if (json.TokenType != JTokenType.Object) return null;
 
-            var entity = Activator.CreateInstance(typeof(T));
-            Object(entity, json, ignoreCase, ignoreCharacters, force);
-            return (T)entity;
+            var entity = Object(typeof(T), json, ignoreCase, ignoreCharacters, force);
+            return entity == null ? default : (T)entity;
         }
 
         /// <summary>将 Json 填充到数组列表,失败时返回 NULL 值。</summary>
-        internal static T[] Array<T>(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false)
+        internal static TItem[] Array<TItem>(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false)
         {
             if (json == null) return null;
             if (json._jtoken == null) return null;
             if (json.TokenType != JTokenType.Array) return null;
 
-            var list = new List<T>();
-            Array(list, json, ignoreCase, ignoreCharacters, force);
-            return list.ToArray();
+            var instance = Array(typeof(TItem), json, ignoreCase, ignoreCharacters, force);
+            var array = (TItem[])instance;
+            return array;
         }
 
-        /// <summary></summary>
-        public static void Object(object entity, Json json, bool ignoreCase, string ignoreCharacters, bool force)
+        static ConstructorInfo GetDeserializeConstructor(Type type)
+        {
+            var types = new Type[] { typeof(Json) };
+            var independent = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, types, null);
+            if (independent == null) independent = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);
+            return independent;
+        }
+
+        /// <summary>创建指定类型的新实例,将 Json 的内容填充到新实例中。</summary>
+        public static object Object(Type type, Json json, bool ignoreCase, string ignoreCharacters, bool force)
         {
-            if (entity == null || json == null) return;
-            if (json.TokenType != JTokenType.Object) return;
+            // 检查参数。
+            if (type == null) throw new ArgumentNullException(nameof(type));
+            if (json == null || json._jtoken == null) return null;
+
+            // 类型自己实现反序列化。
+            var independent = GetDeserializeConstructor(type);
+            if (independent != null) return independent.Invoke(new object[] { json });
+
+            // 是数组。
+            if (type.IsArray)
+            {
+                var itemType = GetTypeOfArrayItem(type);
+                return Array(itemType, json, ignoreCase, ignoreCharacters, force);
+            }
+
+            // 是列表
+            if (typeof(IList).IsAssignableFrom(type))
+            {
+                var genericTypes = type.GetGenericArguments();
+                if (genericTypes != null && genericTypes.Length == 1)
+                {
+                    var genericType = genericTypes[0];
+                    if (genericType != null)
+                    {
+                        var array = Array(genericType, json, ignoreCase, ignoreCharacters, force);
+                        var list = ArrayToList(array, type);
+                        return list;
+                    }
+                }
+            }
+
+            // 必须是有效的 Json 实例。
+            if (json.TokenType != JTokenType.Object && json.TokenType != JTokenType.Array) return null;
+
+            // 必须有无参数的构造函数。
+            var constructor = type.GetConstructor(System.Type.EmptyTypes);
+            if (constructor == null) return null;
+
+            // 创建实例。
+            var entity = constructor.Invoke(null);
 
             var jps = json.GetProperties();
-            if (jps.Length < 1) return;
+            if (jps.Length < 1) return entity;
 
             var etype = entity.GetType();
             var eps = etype.GetProperties();
-            if (eps.Length < 1) return;
+            if (eps.Length < 1) return entity;
 
             foreach (var ep in eps)
             {
@@ -1801,6 +1842,8 @@ namespace Apewer
                     Property(entity, ep, value, ignoreCase, ignoreCharacters, force);
                 }
             }
+
+            return entity;
         }
 
         static void Add(object entity, object item, int index)
@@ -1816,64 +1859,54 @@ namespace Apewer
             }
         }
 
-        internal static void Array(object array, Json json, bool ignoreCase, string ignoreCharacters, bool force)
+        /// <exception cref="ArgumentNullException" />
+        internal static object Array(Type itemType, Json json, bool ignoreCase, string ignoreCharacters, bool force)
         {
-            if (array == null) return;
-            if (json == null) return;
-            if (json.TokenType != JTokenType.Array) return;
+            if (itemType == null) throw new ArgumentNullException(nameof(itemType));
+            if (json == null) return null;
 
-            var type = array.GetType();
-            var subtype = null as Type;
-            if (array is Array)
-            {
-                var arrayType = array.GetType();
-                subtype = RuntimeUtility.GetTypeOfArrayItem(arrayType);
-            }
-            else
-            {
-                var subtypes = type.GetGenericArguments();
-                if (subtypes.Length < 1) return;
-                subtype = subtypes[0];
-            }
-
-            var jis = json.GetItems();
-            for (var index = 0; index < jis.Length; index++)
-            {
-                var ji = jis[index];
-                if (subtype.Equals(typeof(Json))) Add(array, ji, index);
-                else if (subtype.Equals(typeof(string))) Add(array, (ji.TokenType == JTokenType.Null) ? null : ji.Text, index);
-                else if (subtype.Equals(typeof(byte))) Add(array, Byte(ji.Text), index);
-                else if (subtype.Equals(typeof(short))) Add(array, Int16(ji.Text), index);
-                else if (subtype.Equals(typeof(int))) Add(array, Int32(ji.Text), index);
-                else if (subtype.Equals(typeof(long))) Add(array, Int64(ji.Text), index);
-                else if (subtype.Equals(typeof(sbyte))) Add(array, SByte(ji.Text), index);
-                else if (subtype.Equals(typeof(ushort))) Add(array, UInt16(ji.Text), index);
-                else if (subtype.Equals(typeof(uint))) Add(array, UInt32(ji.Text), index);
-                else if (subtype.Equals(typeof(ulong))) Add(array, UInt64(ji.Text), index);
-                else if (subtype.Equals(typeof(float))) Add(array, Single(ji.Text), index);
-                else if (subtype.Equals(typeof(double))) Add(array, Double(ji.Text), index);
-                else if (subtype.Equals(typeof(decimal))) Add(array, Decimal(ji.Text), index);
+            // 必须是有效的 Json 实例。
+            if (json.TokenType != JTokenType.Array) return null;
+
+            // 加入列表。
+            var items = json.GetItems();
+            var list = new List<object>(items.Length);
+            for (var index = 0; index < items.Length; index++)
+            {
+                var item = items[index];
+                if (item == null) list.Add(null);
+                else if (itemType.Equals(typeof(Json))) list.Add(item);
+                else if (itemType.Equals(typeof(string))) list.Add(item ? item.Text : null);
+                else if (itemType.Equals(typeof(byte))) list.Add(Byte(item.Text));
+                else if (itemType.Equals(typeof(short))) list.Add(Int16(item.Text));
+                else if (itemType.Equals(typeof(int))) list.Add(Int32(item.Text));
+                else if (itemType.Equals(typeof(long))) list.Add(Int64(item.Text));
+                else if (itemType.Equals(typeof(sbyte))) list.Add(SByte(item.Text));
+                else if (itemType.Equals(typeof(ushort))) list.Add(UInt16(item.Text));
+                else if (itemType.Equals(typeof(uint))) list.Add(UInt32(item.Text));
+                else if (itemType.Equals(typeof(ulong))) list.Add(UInt64(item.Text));
+                else if (itemType.Equals(typeof(float))) list.Add(Single(item.Text));
+                else if (itemType.Equals(typeof(double))) list.Add(Double(item.Text));
+                else if (itemType.Equals(typeof(decimal))) list.Add(Decimal(item.Text));
                 else
                 {
-                    var serializable = (force || _forceall) ? true : CanSerialize(subtype, false);
-                    if (serializable && (ji is Json))
+                    var serializable = (force || _forceall) ? true : CanSerialize(itemType, false);
+                    if (serializable)
                     {
-                        switch (ji.TokenType)
-                        {
-                            case JTokenType.Object:
-                                var subobject = Activator.CreateInstance(subtype);
-                                Object(subobject, ji, ignoreCase, ignoreCharacters, force);
-                                Add(array, subobject, index);
-                                break;
-                            case JTokenType.Array:
-                                var subarray = Activator.CreateInstance(subtype);
-                                Array(subarray, ji, ignoreCase, ignoreCharacters, force);
-                                Add(array, subarray, index);
-                                break;
-                        }
+                        var itemInstance = Object(itemType, item, ignoreCase, ignoreCharacters, force);
+                        list.Add(itemInstance);
+                    }
+                    else
+                    {
+                        list.Add(null);
                     }
                 }
             }
+
+            // 输出数组。
+            var array = System.Array.CreateInstance(itemType, list.Count);
+            for (var i = 0; i < list.Count; i++) array.SetValue(list[i], i);
+            return array;
         }
 
         private static void Property(object entity, PropertyInfo property, object value, bool ignoreCase, string ignoreCharacters, bool force)
@@ -1930,36 +1963,51 @@ namespace Apewer
                 {
                     var serializable = (force || _forceall);
                     if (!serializable) serializable = CanSerialize(property.PropertyType, false);
-                    if (serializable && (value is Json))
+                    if (serializable && value is Json json)
                     {
-                        switch (((Json)value).TokenType)
+                        if (pt.IsArray)
                         {
-                            case JTokenType.Object:
-                                var subobject = Activator.CreateInstance(property.PropertyType);
-                                Object(subobject, (Json)value, ignoreCase, ignoreCharacters, force);
-                                setter.Invoke(entity, new object[] { subobject });
-                                break;
-                            case JTokenType.Array:
-                                object subarray;
-                                if (pt.BaseType != null && pt.BaseType.Equals(typeof(Array)))
-                                {
-                                    subarray = new object();
-                                    var length = ((Json)value).GetItems().Length;
-                                    subarray = pt.InvokeMember("Set", BindingFlags.CreateInstance, null, subarray, new object[] { length });
-                                }
-                                else
+                            var array = Array(pt.GetElementType(), json, ignoreCase, ignoreCharacters, force);
+                            setter.Invoke(entity, array);
+                        }
+                        else if (typeof(IList).IsAssignableFrom(pt))
+                        {
+                            var genericTypes = pt.GetGenericArguments();
+                            if (genericTypes != null && genericTypes.Length == 1)
+                            {
+                                var genericType = genericTypes[0];
+                                if (genericType != null)
                                 {
-                                    subarray = Activator.CreateInstance(property.PropertyType);
+                                    var array = Array(genericType, json, ignoreCase, ignoreCharacters, force);
+                                    var list = ArrayToList(array, pt);
+                                    setter.Invoke(entity, list);
                                 }
-                                Array(subarray, (Json)value, ignoreCase, ignoreCharacters, force);
-                                setter.Invoke(entity, new object[] { subarray });
-                                break;
+                            }
+                        }
+                        else
+                        {
+                            var @object = Object(pt, json, ignoreCase, ignoreCharacters, force);
+                            setter.Invoke(entity, @object);
                         }
                     }
                 }
             }
         }
 
+        static IList ArrayToList(object array, Type listType)
+        {
+            if (listType == null) throw new ArgumentNullException(nameof(listType));
+
+            if (array != null && array is Array a)
+            {
+                var list = Activator.CreateInstance(listType) as IList;
+                for (var i = 0; i < a.Length; i++) list.Add(a.GetValue(i));
+                return list;
+            }
+
+            return null;
+        }
+
         #endregion
 
         #endregion
diff --git a/Apewer/Network/HttpBody.cs b/Apewer/Network/HttpBody.cs
new file mode 100644
index 0000000..17ccecb
--- /dev/null
+++ b/Apewer/Network/HttpBody.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Apewer.Network
+{
+
+    /// <summary>HTTP 正文。</summary>
+    public abstract class HttpBody { }
+
+    /// <summary>HTTP 报文结构。</summary>
+    public sealed class HttpBytesMessage : HttpBody
+    {
+
+        /// <summary>主体。</summary>
+        public byte[] Bytes { get; set; }
+
+    }
+
+    /// <summary>HTTP 报文结构。</summary>
+    public class HttpStreamMessage<T> : HttpBody where T : Stream
+    {
+
+        /// <summary>自动释放 Stream 对象。</summary>
+        public bool AutoDispose { get; set; }
+
+        /// <summary>主体。</summary>
+        public Stream Stream { get; set; }
+
+        /// <summary>主体的长度。</summary>
+        public long Length { get; set; }
+
+
+    }
+
+    /// <summary>HTTP 报文结构。</summary>
+    public sealed class HttpStreamMessage : HttpBody
+    {
+
+        /// <summary>自动释放 Stream 对象。</summary>
+        public bool AutoDispose { get; set; }
+
+        /// <summary>主体。</summary>
+        public Stream Stream { get; set; }
+
+        /// <summary>主体的长度。</summary>
+        public long Length { get; set; }
+
+    }
+
+}
diff --git a/Apewer/Network/HttpHeader.cs b/Apewer/Network/HttpHeader.cs
new file mode 100644
index 0000000..75c7cbb
--- /dev/null
+++ b/Apewer/Network/HttpHeader.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Network
+{
+
+    /// <summary>HTTP 头。</summary>
+    [Serializable]
+    public sealed class HttpHeader
+    {
+
+        string _name = null;
+        string _value = null;
+
+        /// <summary>名称。</summary>
+        public string Name { get => _name; set => _name = value?.Trim(); }
+
+        /// <summary>值。</summary>
+        public string Value { get => _value; set => _value = value?.Trim(); }
+
+        /// <summary>创建 HTTP 头的实例。</summary>
+        public HttpHeader() { }
+
+        /// <summary>创建 HTTP 头的实例。</summary>
+        /// <exception cref="ArgumentException" />
+        public HttpHeader(KeyValuePair<string, string> keyValuePair)
+        {
+            if (keyValuePair.Key.IsEmpty()) throw new ArgumentNullException("Key 无效。");
+
+            Name = keyValuePair.Key;
+            Value = keyValuePair.Value;
+        }
+
+        /// <summary>创建 HTTP 头的实例。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeader(string name, string value)
+        {
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+            Name = name;
+            Value = value;
+        }
+
+    }
+
+}
diff --git a/Apewer/Network/HttpHeaders.cs b/Apewer/Network/HttpHeaders.cs
new file mode 100644
index 0000000..3d1f9b0
--- /dev/null
+++ b/Apewer/Network/HttpHeaders.cs
@@ -0,0 +1,369 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+
+namespace Apewer.Network
+{
+
+    /// <summary>HTTP 头的集合。</summary>
+    [Serializable]
+    public sealed class HttpHeaders : IEnumerable<HttpHeader>, ICollection<HttpHeader>, IToJson
+    {
+
+        #region IEnumerable
+
+        List<HttpHeader> _list = new List<HttpHeader>();
+        long _version = 0L;
+
+        /// <summary>获取枚举器。</summary>
+        public IEnumerator<HttpHeader> GetEnumerator() => new Enumerator(this);
+
+        /// <summary>获取枚举器。</summary>
+        IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
+
+        /// <summary></summary>
+        public sealed class Enumerator : IEnumerator<HttpHeader>
+        {
+
+            HttpHeaders _headeres = null;
+            long _version = 0L;
+            bool _disposed = false;
+
+            HttpHeader _current = null;
+            int _index = 0;
+
+            const string ObjectName = nameof(Enumerator);
+            const string OriginChanged = "原集合已变更,无法继续遍历。";
+
+            /// <summary></summary>
+            public HttpHeader Current
+            {
+                get
+                {
+                    if (_disposed) throw new ObjectDisposedException(ObjectName);
+                    if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
+                    return _current;
+                }
+            }
+
+            /// <summary></summary>
+            object IEnumerator.Current { get => Current; }
+
+            /// <summary></summary>
+            public void Dispose() { _disposed = true; }
+
+            /// <summary></summary>
+            public bool MoveNext()
+            {
+                if (_disposed) throw new ObjectDisposedException(ObjectName);
+                if (_version != _headeres._version) throw new InvalidOperationException(OriginChanged);
+
+                if (_index < _headeres._list.Count)
+                {
+                    _current = _headeres._list[_index];
+                    _index++;
+                    return true;
+                }
+
+                return false;
+            }
+
+            /// <summary></summary>
+            public void Reset()
+            {
+                _current = null;
+                _index = 0;
+            }
+
+            /// <exception cref="ArgumentNullException" />
+            public Enumerator(HttpHeaders headers)
+            {
+                if (headers == null) throw new ArgumentNullException(nameof(headers));
+                _headeres = headers;
+                _version = headers._version;
+                Reset();
+            }
+
+        }
+
+        #endregion
+
+        #region ICollection
+
+        /// <summary>元素数量。</summary>
+        public int Count { get => _list.Count; }
+
+        /// <summary>当前集合是只读。</summary>
+        public bool IsReadOnly { get => false; }
+
+        /// <summary>添加一项。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeader Add(string name, string value)
+        {
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+            var header = new HttpHeader(name, value);
+            _list.Add(header);
+            _version++;
+            return header;
+        }
+
+        /// <summary>添加一项。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public void Add(HttpHeader header)
+        {
+            if (header == null) throw new ArgumentNullException(nameof(header));
+            _list.Add(header);
+            _version++;
+        }
+
+        /// <summary>移除所有元素。</summary>
+        public void Clear() => _list.Clear();
+
+        /// <summary>判断是否包含指定的元素。</summary>
+        public bool Contains(HttpHeader item) => _list.Contains(item);
+
+        /// <summary>复制所有元素到数组。</summary>
+        /// <param name="array">目标数组。</param>
+        /// <param name="arrayIndex">数组的位置。</param>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="ArgumentOutOfRangeException" />
+        /// <exception cref="ArgumentException" />
+        public void CopyTo(HttpHeader[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
+
+        /// <summary>移除指定的元素</summary>
+        public bool Remove(HttpHeader item) => _list.Remove(item);
+
+        #endregion
+
+        #region IList
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentOutOfRangeException" />
+        public HttpHeader this[int index] { get => _list[index]; set => _list[index] = value; }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentOutOfRangeException" />
+        public string this[string name] { get => GetValue(name); set => SetValue(name, value); }
+
+        /// <summary>搜索指定对象在当前集合中的位置索引。</summary>
+        public int IndexOf(HttpHeader item) => _list.IndexOf(item);
+
+        /// <summary>插入元素到指定位置。</summary>
+        /// <exception cref="ArgumentOutOfRangeException" />
+        public void Insert(int index, HttpHeader item) => _list.Insert(index, item);
+
+        /// <summary>移除指定位置的元素。</summary>
+        /// <exception cref="ArgumentOutOfRangeException" />
+        public void RemoveAt(int index) => _list.RemoveAt(index);
+
+        #endregion
+
+        #region constructor
+
+        /// <summary></summary>
+        public HttpHeaders() { }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeaders(params HttpHeader[] headers) : this(headers as IEnumerable<HttpHeader>) { }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeaders(IEnumerable<HttpHeader> headers)
+        {
+            if (headers == null) throw new ArgumentNullException(nameof(headers));
+
+            foreach (var header in headers)
+            {
+                if (header == null) continue;
+                if (header.Name.IsEmpty()) continue;
+                Add(header);
+            }
+        }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeaders(IEnumerable<KeyValuePair<string, string>> headers)
+        {
+            if (headers == null) return;
+
+            foreach (var header in headers)
+            {
+                if (header.Key.IsEmpty()) continue;
+                Add(header.Key, header.Value);
+            }
+        }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeaders(NameValueCollection headers)
+        {
+            if (headers == null) return;
+
+            var keys = headers.AllKeys;
+            foreach (var key in keys)
+            {
+                if (key.IsEmpty()) continue;
+                var value = headers[key];
+                Add(key, value);
+            }
+        }
+
+        #endregion
+
+        #region operation
+
+        /// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public string GetValue(string name)
+        {
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+            // 精准匹配。
+            var count = _list.Count;
+            for (var i = 0; i < count; i++)
+            {
+                var item = _list[i];
+                if (string.IsNullOrEmpty(item.Name)) continue;
+                if (item.Name == name)
+                {
+                    if (string.IsNullOrEmpty(item.Value)) continue;
+                    return item.Value;
+                }
+            }
+
+            // 忽略大小写。
+            var lower = TextUtility.Lower(name);
+            for (var i = 0; i < count; i++)
+            {
+                var item = _list[i];
+                if (string.IsNullOrEmpty(item.Name)) continue;
+                if (TextUtility.Lower(item.Name) == lower)
+                {
+                    if (string.IsNullOrEmpty(item.Value)) continue;
+                    return item.Value;
+                }
+            }
+
+            return null;
+        }
+
+        /// <summary>获取匹配 Name 的 Value。不存在 Name 时返回 NULL 值。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public string[] GetValues(string name)
+        {
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+            // 忽略大小写。
+            // name = TextUtility.Lower(name);
+
+            var count = _list.Count;
+            var values = new List<string>(_list.Count);
+            for (var i = 0; i < count; i++)
+            {
+                var item = _list[i];
+                if (string.IsNullOrEmpty(item.Name)) continue;
+                if (string.Equals(item.Name, name, StringComparison.CurrentCultureIgnoreCase))
+                {
+                    var value = item.Value.ToTrim();
+                    if (string.IsNullOrEmpty(value)) continue;
+                    values.Add(value);
+                }
+            }
+
+            return values.ToArray();
+        }
+
+        /// <summary>设置 Name 的 Value。不存在 Name 时添加新元素。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public HttpHeader SetValue(string name, string value)
+        {
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+            // 尝试精准匹配。
+            var count = _list.Count;
+            for (var i = 0; i < count; i++)
+            {
+                var item = _list[i];
+                if (string.IsNullOrEmpty(item.Name)) continue;
+                if (item.Name == name)
+                {
+                    item.Value = value;
+                    _version++;
+                    return item;
+                }
+            }
+
+            // 尝试模糊匹配。
+            var lower = TextUtility.Lower(name);
+            for (var i = 0; i < count; i++)
+            {
+                var item = _list[i];
+                if (TextUtility.Lower(item.Name) == lower)
+                {
+                    item.Value = value;
+                    _version++;
+                    return item;
+                }
+            }
+
+            // 添加新项。
+            return Add(name, value);
+        }
+
+        /// <summary>每个元素组成为新数组。</summary>
+        public HttpHeader[] ToArray() => _list.ToArray();
+
+        #endregion
+
+        #region Json
+
+        internal HttpHeaders(Json json)
+        {
+            if (!json) return;
+
+            if (json.IsObject)
+            {
+                var properties = json.GetProperties();
+                foreach (var property in properties)
+                {
+                    if (property == null) continue;
+                    if (property.Name.IsEmpty()) continue;
+                    Add(property.Name, property.Value?.ToString());
+                }
+            }
+
+            if (json.IsArray)
+            {
+                var array = json.Array<HttpHeader>();
+                if (array != null)
+                {
+                    for (var i = 0; i < array.Length; i++)
+                    {
+                        if (array[i] == null) continue;
+                        if (array[i].Name.IsEmpty()) continue;
+                        _list.Add(array[i]);
+                    }
+                }
+            }
+        }
+
+        /// <summary></summary>
+        public Json ToJson()
+        {
+            var array = Json.NewArray();
+            var count = 0;
+            for (var i = 0; i < count; i++)
+            {
+                var item = Json.From(_list[i]);
+                array.AddItem(item);
+            }
+            return array;
+        }
+
+        #endregion
+
+    }
+
+}
diff --git a/Apewer/Network/HttpMethod.cs b/Apewer/Network/HttpMethod.cs
index 5c7c9e4..ae08438 100644
--- a/Apewer/Network/HttpMethod.cs
+++ b/Apewer/Network/HttpMethod.cs
@@ -11,31 +11,31 @@ namespace Apewer.Network
         /// <summary></summary>
         NULL,
 
-        /// <summary>The CONNECT method establishes a tunnel to the server identified by the target resource.</summary>
+        /// <summary>CONNECT 方法建立一个到由目标资源标识的服务器的隧道。</summary>
         CONNECT,
 
-        /// <summary>The DELETE method deletes the specified resource.</summary>
+        /// <summary>DELETE 方法删除指定的资源。</summary>
         DELETE,
 
-        /// <summary>The GET method requests a representation of the specified resource. Requests using GET should only retrieve data.</summary>
+        /// <summary>GET 方法请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。</summary>
         GET,
 
-        /// <summary>The HEAD method asks for a response identical to that of a GET request, but without the response body.</summary>
+        /// <summary>HEAD 方法请求一个与 GET 请求的响应相同的响应,但没有响应体。</summary>
         HEAD,
 
-        /// <summary>The OPTIONS method is used to describe the communication options for the target resource.</summary>
+        /// <summary>OPTIONS 方法用于描述目标资源的通信选项。</summary>
         OPTIONS,
 
-        /// <summary>The PATCH method is used to apply partial modifications to a resource.</summary>
+        /// <summary>PATCH 方法用于对资源应用部分修改。</summary>
         PATCH,
 
-        /// <summary>The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server.</summary>
+        /// <summary>POST 方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。</summary>
         POST,
 
-        /// <summary>The PUT method replaces all current representations of the target resource with the request payload.</summary>
+        /// <summary>PUT 方法用有效载荷请求替换目标资源的所有当前表示。</summary>
         PUT,
 
-        /// <summary>The TRACE method performs a message loop-back test along the path to the target resource.</summary>
+        /// <summary>TRACE 方法沿着到目标资源的路径执行一个消息环回测试。</summary>
         TRACE
 
     }
diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs
index eb24203..bd837c5 100644
--- a/Apewer/RuntimeUtility.cs
+++ b/Apewer/RuntimeUtility.cs
@@ -229,32 +229,14 @@ namespace Apewer
         /// <exception cref="TargetException"></exception>
         /// <exception cref="TargetInvocationException"></exception>
         /// <exception cref="TargetParameterCountException"></exception>
-        public static object InvokeMethod(object instance, MethodInfo method, object parameter)
+        public static object Invoke(this MethodInfo method, object instance, params object[] parameters)
         {
             if (instance == null || method == null) return null;
-            {
-                var pis = method.GetParameters();
-                if (pis == null || pis.Length < 1) return method.Invoke(instance, null);
-            }
-            return method.Invoke(instance, new object[] { parameter });
-        }
 
-        /// <summary>调用方法。</summary>
-        /// <exception cref="ArgumentException"></exception>
-        /// <exception cref="InvalidOperationException"></exception>
-        /// <exception cref="MethodAccessException"></exception>
-        /// <exception cref="NotSupportedException"></exception>
-        /// <exception cref="TargetException"></exception>
-        /// <exception cref="TargetInvocationException"></exception>
-        /// <exception cref="TargetParameterCountException"></exception>
-        public static object InvokeMethod(object instance, MethodInfo method, params object[] parameters)
-        {
-            if (instance == null || method == null) return null;
-            {
-                var pis = method.GetParameters();
-                if (pis == null || pis.Length < 1) return method.Invoke(instance, null);
-            }
-            if (parameters == null) return method.Invoke(instance, new object[] { null });
+            var pis = method.GetParameters();
+            if (pis == null || pis.Length < 1) return method.Invoke(instance, null);
+
+            if (parameters == null) return method.Invoke(instance, null);
             return method.Invoke(instance, parameters);
         }
 
diff --git a/Apewer/Web/ActionResult.cs b/Apewer/Web/ActionResult.cs
new file mode 100644
index 0000000..3c9d74c
--- /dev/null
+++ b/Apewer/Web/ActionResult.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>API 结果。</summary>
+    public abstract class ActionResult : IActionResult, IHttpActionResult, IDisposable
+    {
+
+        /// <summary>释放系统资源。</summary>
+        public virtual void Dispose() { }
+
+        // /// <summary>释放系统资源。</summary>
+        // /// <param name="disposing">TRUE:释放托管资源和非托管资源。FALSE:释放非托管资源。</param>
+        // protected virtual void Dispose(bool disposing) { }
+
+        /// <summary>执行结果。</summary>
+        public abstract void ExecuteResult(ApiContext context);
+
+    }
+
+}
diff --git a/Apewer/Web/ApiAction.cs b/Apewer/Web/ApiAction.cs
new file mode 100644
index 0000000..26db784
--- /dev/null
+++ b/Apewer/Web/ApiAction.cs
@@ -0,0 +1,240 @@
+using Apewer.Network;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>API 行为。</summary>
+    public sealed class ApiAction : IToJson
+    {
+
+        #region fields
+
+        Type _type = null;
+        MethodInfo _method = null;
+
+        string _path = null;
+        HttpMethod[] _methods = null;
+        ApiParameter[] _parameters = null;
+
+        #endregion
+
+        #region propeties
+
+        /// <summary>控制器的反射类型。</summary>
+        public Type Type { get => _type; }
+
+        /// <summary>API 行为的反射方法。</summary>
+        public MethodInfo MethodInfo { get => _method; }
+
+        /// <summary>URL 路径。</summary>
+        public string Path { get => _path; }
+
+        /// <summary>HTTP 方法。</summary>
+        public HttpMethod[] Methods
+        {
+            get
+            {
+                var result = new HttpMethod[_methods.Length];
+                if (_methods.Length > 0) _methods.CopyTo(result, 0);
+                return result;
+            }
+        }
+
+        /// <summary>参数。</summary>
+        public ApiParameter[] Parameters
+        {
+            get
+            {
+                var result = new ApiParameter[_parameters.Length];
+                if (_parameters.Length > 0) _parameters.CopyTo(result, 0);
+                return result;
+            }
+        }
+
+        /// <summary>生成 JSON 实例。</summary>
+        public Json ToJson() => ToJson(null);
+
+        /// <summary>生成 JSON 实例。</summary>
+        public Json ToJson(ApiActionJsonFormat format)
+        {
+            if (format == null) format = ApiActionJsonFormat.Default ?? new ApiActionJsonFormat();
+
+            var methods = new Json();
+            foreach (var method in _methods)
+            {
+                methods.AddItem(method.ToString().Lower());
+            }
+
+            var json = new Json();
+            json.SetProperty("path", _path);
+            json.SetProperty("methods", methods);
+
+            if (format.WithReflection)
+            {
+                var reflection = new Json();
+                reflection.SetProperty("type", _type.FullName);
+                reflection.SetProperty("method", _method.Name);
+                json.SetProperty("reflection", reflection);
+            }
+
+            if (format.WithParameters && _parameters.Length > 0)
+            {
+                var parameters = Json.NewArray();
+                foreach (var parameter in _parameters)
+                {
+                    parameters.AddItem(parameter.ToJson(format.WithReflection));
+                }
+                json.SetProperty("parameters", parameters);
+            }
+
+            return json;
+        }
+
+        /// <summary>生成字符串。</summary>
+        public override string ToString() => _path;
+
+        #endregion
+
+        #region parse
+
+        const string Separator = "/";
+
+        /// <summary>创建 API 行为描述实例。</summary>
+        /// <param name="type">控制器的反射类型。</param>
+        /// <param name="method">API 行为的反射方法。</param>
+        /// <param name="path">URL 路径。</param>
+        /// <param name="methods">HTTP 方法。</param>
+        /// <param name="parameters">参数。</param>
+        /// <exception cref="ArgumentNullException" />
+        /// <exception cref="ArgumentException" />
+        ApiAction(Type type, MethodInfo method, string path, HttpMethod[] methods, ApiParameter[] parameters)
+        {
+            if (type == null) throw new ArgumentNullException(nameof(type));
+            if (method == null) throw new ArgumentNullException(nameof(method));
+            if (method.IsAbstract) throw new ArgumentException($"参数 {nameof(method)} 是抽象的。");
+            if (path.IsEmpty()) throw new ArgumentNullException(nameof(path));
+            if (methods.IsEmpty()) throw new ArgumentNullException(nameof(methods));
+
+            var split = path.Split('/');
+            var segs = split.Trim();
+            path = segs.Length < 1 ? Separator : (Separator + string.Join(Separator, segs));
+
+            _type = type;
+            _method = method;
+            _path = path;
+            _methods = methods;
+            _parameters = parameters;
+        }
+
+        /// <summary>解析控制器类型,获取 API 活动。</summary>
+        /// <exception cref="ArgumentNullException" />
+        public static ApiAction[] Parse(Type type)
+        {
+            if (type == null) throw new ArgumentNullException(nameof(type));
+
+            if (type.FullName == "Front.Debug.Controller")
+            {
+            }
+
+            // 检查类型的属性。
+            if (!type.IsClass) return new ApiAction[0];
+            if (type.IsAbstract) return new ApiAction[0];
+            if (type.IsGenericType) return new ApiAction[0];
+            if (type.GetGenericArguments().NotEmpty()) return new ApiAction[0];
+            if (!RuntimeUtility.CanNew(type)) return new ApiAction[0];
+
+            // 判断基类。
+            if (!typeof(ApiController).IsAssignableFrom(type)) return new ApiAction[0];
+
+            // 读取 URL 前缀。
+            var prefixAttribute = RuntimeUtility.GetAttribute<RoutePrefixAttribute>(type, false);
+            var prefixPath = (prefixAttribute == null || prefixAttribute.Path.IsEmpty()) ? null : prefixAttribute.Path.Split('/').Trim();
+            // if (prefixPath.IsEmpty())
+            // {
+            //     // 读取 API 特性。
+            //     var api = RuntimeUtility.GetAttribute<ApiAttribute>(type);
+            //     if (api != null)
+            //     {
+            //         var apiName = api.Name.ToTrim();
+            //         if (apiName.Lower().EndsWith("controller")) apiName = apiName.Substring(0, apiName.Length - 10);
+            //         if (apiName.NotEmpty()) prefixPath = new string[] { apiName };
+            //     }
+            // }
+
+            // 读取方法。
+            var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
+            var actions = new List<ApiAction>(methods.Length);
+            foreach (var method in methods)
+            {
+                // 不支持构造函数和泛型。
+                if (method.IsConstructor) continue;
+                if (method.IsGenericMethod) continue;
+
+                // 抽象类无法创建实例。
+                if (method.IsAbstract) continue;
+                if (!method.DeclaringType.Equals(type)) continue;
+
+                // 必须有 Route 特性
+                var route = RuntimeUtility.GetAttribute<RouteAttribute>(method);
+                if (route == null) continue;
+
+                // 确定路径
+                var path = route.Path;
+                if (path.IsEmpty())
+                {
+                    if (prefixPath.IsEmpty()) continue;
+                    path = method.Name;
+                }
+                path = ConcatPath(prefixPath, path.Split('/').Trim());
+
+                // 必须有 HTTP 方法
+                var httpMethods = new List<HttpMethod>(9);
+                if (RuntimeUtility.Contains<HttpConnectAttribute>(method)) httpMethods.Add(HttpMethod.CONNECT);
+                if (RuntimeUtility.Contains<HttpDeleteAttribute>(method)) httpMethods.Add(HttpMethod.DELETE);
+                if (RuntimeUtility.Contains<HttpGetAttribute>(method)) httpMethods.Add(HttpMethod.GET);
+                if (RuntimeUtility.Contains<HttpHeadAttribute>(method)) httpMethods.Add(HttpMethod.HEAD);
+                if (RuntimeUtility.Contains<HttpOptionsAttribute>(method)) httpMethods.Add(HttpMethod.OPTIONS);
+                if (RuntimeUtility.Contains<HttpPatchAttribute>(method)) httpMethods.Add(HttpMethod.PATCH);
+                if (RuntimeUtility.Contains<HttpPostAttribute>(method)) httpMethods.Add(HttpMethod.POST);
+                if (RuntimeUtility.Contains<HttpPutAttribute>(method)) httpMethods.Add(HttpMethod.PUT);
+                if (RuntimeUtility.Contains<HttpTraceAttribute>(method)) httpMethods.Add(HttpMethod.TRACE);
+                if (httpMethods.Count < 1) continue;
+
+                // 参数
+                var parameters = new List<ApiParameter>();
+                foreach (var pi in method.GetParameters())
+                {
+                    var parameter = ApiParameter.Parse(pi);
+                    if (parameter == null) continue;
+                    parameters.Add(parameter);
+                }
+
+                var action = new ApiAction(type, method, path, httpMethods.ToArray(), parameters.ToArray());
+                actions.Add(action);
+            }
+
+            return actions.ToArray();
+        }
+
+        static string ConcatPath(string[] prefix, string[] path)
+        {
+            var list = new List<string>();
+            if (prefix != null) list.AddRange(prefix);
+            if (path != null) list.AddRange(path);
+
+            var segs = list.Trim();
+            if (segs.Length < 1) return Separator;
+
+            var result = Separator + string.Join(Separator, segs);
+            return result;
+        }
+
+        #endregion
+
+    }
+
+}
diff --git a/Apewer/Web/ApiActionJsonFormat.cs b/Apewer/Web/ApiActionJsonFormat.cs
new file mode 100644
index 0000000..d4b82b1
--- /dev/null
+++ b/Apewer/Web/ApiActionJsonFormat.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>类型 <see cref="ApiAction"/> 的实例转为 <see cref="Json"/> 时的格式。</summary>
+    [Serializable]
+    public sealed class ApiActionJsonFormat
+    {
+
+        /// <summary>默认格式。</summary>
+        public static ApiActionJsonFormat Default { get; set; }
+
+        /// <summary>包含参数。</summary>
+        /// <value>TRUE(默认值)</value>
+        public bool WithParameters { get; set; }
+
+        /// <summary>包含反射信息。</summary>
+        /// <value>TRUE(默认值)</value>
+        public bool WithReflection { get; set; }
+
+        /// <summary>创建 <see cref="ApiActionJsonFormat"/> 的实例。</summary>
+        public ApiActionJsonFormat()
+        {
+            WithParameters = true;
+            WithReflection = true;
+        }
+
+    }
+
+}
diff --git a/Apewer/Web/ApiApplication.cs b/Apewer/Web/ApiApplication.cs
index a598c8e..605eb4e 100644
--- a/Apewer/Web/ApiApplication.cs
+++ b/Apewer/Web/ApiApplication.cs
@@ -5,47 +5,176 @@ using System.Reflection;
 namespace Apewer.Web
 {
 
-    internal sealed class ApiApplication
+    /// <summary></summary>
+    public sealed class ApiApplication : IToJson
     {
 
-        internal Dictionary<string, ApiFunction> Functions = null;
-        internal List<ApiFunction> Items = null;
+        #region fields
 
-        internal Type Type;
-        internal string Module;
+        Type _type = null;
+        string _module = null;
 
-        // 主特性和主要属性。
-        internal ApiAttribute Attribute;
-        internal string Name;
-        internal string Lower;
-        internal string Caption;
-        internal string Description;
+        // ApiAttribute
+        string _name = null;
+        string _lower = null;
+        string _caption = null;
+        string _description = null;
 
-        // 附加特性。
-        internal bool Independent;
-        internal bool Hidden;
+        // invoke & enumerate
+        bool _independent = false;
+        bool _hidden = false;
 
-        internal ApiFunction Get(string name)
+        // functions
+        Dictionary<string, ApiFunction> _dict = new Dictionary<string, ApiFunction>();
+        List<ApiFunction> _list = new List<ApiFunction>();
+
+        #endregion
+
+        #region properties
+
+        /// <summary></summary>
+        public Type Type { get => _type; }
+
+        /// <summary></summary>
+        public string Module { get => _module; }
+
+        /// <summary></summary>
+        public string Name { get => _name; }
+
+        /// <summary></summary>
+        public string Caption { get => _caption; }
+
+        /// <summary></summary>
+        public string Description { get => _description; }
+
+        /// <summary></summary>
+        public bool Independent { get => _independent; }
+
+        /// <summary></summary>
+        public bool Hidden { get => _hidden; }
+
+        /// <summary></summary>
+        public ApiFunction[] Functions { get => _list.ToArray(); }
+
+        #endregion
+
+        /// <summary></summary>
+        public ApiApplication(Type type, ApiAttribute api)
+        {
+            // type
+            _type = type;
+
+            // api
+            if (api == null)
+            {
+                _name = type?.Name;
+            }
+            else
+            {
+                _name = string.IsNullOrEmpty(api.Name) ? type?.Name : api.Name;
+                _caption = api.Caption;
+                _description = api.Description;
+            }
+
+            if (type != null)
+            {
+                // caption
+                if (string.IsNullOrEmpty(_caption))
+                {
+                    var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
+                    if (captions.Length > 0)
+                    {
+                        var caption = (CaptionAttribute)captions[0];
+                        _caption = caption.Title;
+                        if (string.IsNullOrEmpty(_description))
+                        {
+                            _description = caption.Description;
+                        }
+                    }
+                }
+
+                // hidden
+                if (type.Contains<HiddenAttribute>(false)) _hidden = true;
+
+                // independent
+                if (type.Contains<IndependentAttribute>(false)) _independent = true;
+
+                // Module
+                var assemblyName = type.Assembly.GetName();
+                _module = TextUtility.Join("-", assemblyName.Name, assemblyName.Version.ToString());
+
+                // functions
+                var funcs = new Dictionary<string, ApiFunction>();
+                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
+                foreach (var method in methods)
+                {
+                    var func = ApiFunction.Parse(this, method);
+                    if (func == null) continue;
+
+                    var funcKey = func.Name.Lower();
+                    if (funcs.ContainsKey(funcKey)) continue;
+                    funcs.Add(funcKey, func);
+                }
+                _dict = funcs;
+
+                _list.AddRange(funcs.Values);
+                _list.Sort(new Comparison<ApiFunction>((a, b) => a.Name.CompareTo(b.Name)));
+            }
+        }
+
+        internal ApiFunction GetFunction(string name)
         {
             if (string.IsNullOrEmpty(name)) return null;
-            var lower = name.ToLower();
-            ApiFunction func;
-            var exist = Functions.TryGetValue(lower, out func);
-            return func;
+            if (_dict.TryGetValue(name.ToLower(), out var func)) return func;
+            return null;
         }
 
+        /// <summary></summary>
+        public Json ToJson() => ToJson(new ApiOptions());
+
         internal Json ToJson(ApiOptions options)
         {
             if (Hidden) return null;
+
             var json = Json.NewObject();
-            json.SetProperty("name", Name);
-            if (!string.IsNullOrEmpty(Caption)) json.SetProperty("caption", Caption);
-            if (!string.IsNullOrEmpty(Description)) json.SetProperty("description", Description);
-            if (options.WithTypeName) json.SetProperty("type", Type.FullName);
-            if (options.WithModuleName) json.SetProperty("mudule", Module);
+            json.SetProperty("name", _name);
+            if (!string.IsNullOrEmpty(_caption)) json.SetProperty("caption", _caption);
+            if (!string.IsNullOrEmpty(_description)) json.SetProperty("description", _description);
+            if (options != null)
+            {
+                if (options.WithTypeName) json.SetProperty("type", _type.FullName);
+                if (options.WithModuleName) json.SetProperty("mudule", _module);
+                if (options.AllowEnumerate) json.SetProperty("functions", Json.From(_list));
+            }
             return json;
         }
 
+        /// <summary>解析类型,获取 <see cref="ApiApplication"/> 实例。</summary>
+        /// <param name="type">要解析的类型。</param>
+        /// <param name="requireAttribute">要求此类型拥有 Api 特性。</param>
+        /// <returns>解析成功返回实例,解析失败返回 NULL 值。</returns>
+        public static ApiApplication Parse(Type type, bool requireAttribute)
+        {
+            if (type == null) return null;
+
+            // 检查类型的属性。
+            if (!type.IsClass) return null;
+            if (type.IsAbstract) return null;
+            if (type.IsGenericType) return null;
+            if (type.GetGenericArguments().NotEmpty()) return null;
+            if (!RuntimeUtility.CanNew(type)) return null;
+
+            // 判断基类。
+            if (!typeof(ApiController).IsAssignableFrom(type)) return null;
+
+            // 检查类型的特性。
+            var apis = type.GetCustomAttributes(typeof(ApiAttribute), false);
+            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
+            if (requireAttribute && api == null) return null;
+
+            return new ApiApplication(type, api);
+        }
+
     }
 
 }
diff --git a/Apewer/Web/ApiCatch.cs b/Apewer/Web/ApiCatch.cs
index f5a632b..d214ff5 100644
--- a/Apewer/Web/ApiCatch.cs
+++ b/Apewer/Web/ApiCatch.cs
@@ -9,23 +9,18 @@ namespace Apewer.Web
     public sealed class ApiCatch
     {
 
-        ApiController _controller = null;
-        ApiOptions _options = null;
+        ApiContext _context = null;
         Exception _exception = null;
 
-        /// <summary>调度程序调用的控制器。</summary>
-        public ApiController Controller { get => _controller; }
-
-        /// <summary>调度程序使用的 API 选项。</summary>
-        public ApiOptions Options { get => _options; }
+        /// <summary>上下文。</summary>
+        public ApiContext Context { get => _context; }
 
         /// <summary>已捕获的异常。</summary>
         public Exception Exception { get => _exception; }
 
-        internal ApiCatch(ApiController controller, ApiOptions options, Exception exception)
+        internal ApiCatch(ApiContext context, Exception exception)
         {
-            _controller = controller;
-            _options = options;
+            _context = context;
             _exception = exception;
         }
 
diff --git a/Apewer/Web/ApiContext.cs b/Apewer/Web/ApiContext.cs
index 86ff90e..424b875 100644
--- a/Apewer/Web/ApiContext.cs
+++ b/Apewer/Web/ApiContext.cs
@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Reflection;
 using System.Text;
 
 namespace Apewer.Web
@@ -39,8 +40,8 @@ namespace Apewer.Web
 
         #region 执行过程中产生的内容
 
-        /// <summary>API 入口。</summary>
-        public ApiEntry Entry { get; internal set; }
+        /// <summary>API 行为。</summary>
+        public ApiAction ApiAction { get; internal set; }
 
         /// <summary>API 请求。</summary>
         public ApiRequest Request { get; internal set; }
@@ -51,6 +52,9 @@ namespace Apewer.Web
         /// <summary>API 控制器实例。</summary>
         public ApiController Controller { get; internal set; }
 
+        /// <summary>执行的方法。</summary>
+        public MethodInfo MethodInfo { get; internal set; }
+
         #endregion
 
         internal ApiContext(ApiInvoker invoker, ApiProvider provider, ApiEntries entries)
diff --git a/Apewer/Web/ApiEntries.cs b/Apewer/Web/ApiEntries.cs
index 28624ba..94a3edd 100644
--- a/Apewer/Web/ApiEntries.cs
+++ b/Apewer/Web/ApiEntries.cs
@@ -7,272 +7,181 @@ namespace Apewer.Web
 {
 
     /// <summary>入口集合。</summary>
-    public sealed class ApiEntries
+    public sealed class ApiEntries : IToJson
     {
 
-        #region 实例。
+        #region instance
 
         object locker = new object();
 
-        Dictionary<string, ApiApplication> Applications = null;
-        List<ApiApplication> Items = null;
+        SortedDictionary<string, ApiApplication> _apps = new SortedDictionary<string, ApiApplication>();
+        SortedDictionary<string, ApiAction> _actions = new SortedDictionary<string, ApiAction>();
 
-        internal ApiApplication Get(string name)
+        internal ApiApplication GetApplication(string name)
         {
             if (string.IsNullOrEmpty(name)) return null;
-            if (Applications == null) return null;
-            var lower = name.ToLower();
-            ApiApplication app;
-            var found = false;
-            lock (locker) { found = Applications.TryGetValue(lower, out app); }
-            return found ? app : null;
-        }
-
-        internal List<ApiApplication> Enumerate() => Items;
 
-        /// <summary>清空当前实例。</summary>
-        public void Clear()
-        {
+            var key = name.ToLower();
             lock (locker)
             {
-                Applications = new Dictionary<string, ApiApplication>();
-                Items = new List<ApiApplication>();
+                if (_apps.TryGetValue(key, out var value)) return value;
             }
+            return null;
         }
 
-        /// <summary>追加指定的集合,指定 replace 参数将替换当前实例中的同名的入口。</summary>
-        public void Append(ApiEntries entries, bool replace = false)
+        internal ApiAction GetAction(string path)
         {
-            if (entries == null) return;
+            if (string.IsNullOrEmpty(path)) return null;
+
+            var key = path.ToLower();
             lock (locker)
             {
-                var dict = Applications ?? new Dictionary<string, ApiApplication>();
-                foreach (var app in entries.Applications)
-                {
-                    var key = app.Key;
-                    if (dict.ContainsKey(key))
-                    {
-                        if (replace) dict[key] = app.Value;
-                        continue;
-                    }
-                    dict.Add(app.Key, app.Value);
-                }
-                var list = new List<ApiApplication>(dict.Values);
-                list.Sort(new Comparison<ApiApplication>((a, b) => a.Lower.CompareTo(b.Lower)));
-                Applications = dict;
-                Items = list;
+                if (_actions.TryGetValue(key, out var value)) return value;
             }
+            return null;
         }
 
-        #endregion
+        /// <summary></summary>
+        public ApiApplication[] Applications { get => _apps.Values.Map(x => x); }
 
-        #region 静态方法。
+        /// <summary></summary>
+        public ApiAction[] Actions { get => _actions.Values.Map(x => x); }
 
-        /// <summary>从指定的程序集获取入口。</summary>
-        public static ApiEntries From(Assembly assembly)
-        {
-            if (assembly == null) return null;
-            var types = RuntimeUtility.GetTypes(assembly, false);
-            var dict = new Dictionary<string, ApiApplication>();
-            foreach (var type in types)
-            {
-                var app = Application(type, true);
-                if (app == null) continue;
-                var lower = app.Lower;
-                if (dict.ContainsKey(lower)) continue;
-                dict.Add(lower, app);
-
-                var funcs = new Dictionary<string, ApiFunction>();
-                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
-                foreach (var method in methods)
-                {
-                    var func = Function(app, method);
-                    if (func == null) continue;
-                    if (funcs.ContainsKey(func.Lower)) continue;
-                    funcs.Add(func.Lower, func);
-                }
-                app.Functions = funcs;
-                app.Items = new List<ApiFunction>(funcs.Values);
-                app.Items.Sort(new Comparison<ApiFunction>((a, b) => a.Name.CompareTo(b.Name)));
-            }
-            var entries = new ApiEntries();
-            entries.Applications = dict;
-            var list = new List<ApiApplication>(dict.Values);
-            list.Sort(new Comparison<ApiApplication>((a, b) => a.Lower.CompareTo(b.Lower)));
-            entries.Items = list;
-            return entries;
-        }
+        /// <summary></summary>
+        public ApiEntries() { }
 
-        /// <summary>从多个程序集中获取入口。</summary>
-        public static ApiEntries From(IEnumerable<Assembly> assemblies, bool replace = false)
+        /// <summary></summary>
+        public ApiEntries(IEnumerable<ApiApplication> applications, IEnumerable<ApiAction> actions, bool replace = false) : this()
         {
-            if (assemblies == null) return null;
-            var entries = new ApiEntries();
-            foreach (var assembly in assemblies) entries.Append(From(assembly), replace);
-            return entries;
+            Add(applications, replace);
+            Add(actions, replace);
         }
 
-        /// <summary>从当前程序中获取入口。 </summary>
-        public static ApiEntries Calling() => From(Assembly.GetCallingAssembly());
+        /// <summary></summary>
+        public ApiEntries(IEnumerable<ApiApplication> applications, bool replace = false) : this(applications, null, replace) { }
 
-        /// <summary>从当前 AppDomain 中获取入口。</summary>
-        public static ApiEntries AppDomain(bool replace = false) => From(System.AppDomain.CurrentDomain.GetAssemblies(), replace);
+        /// <summary></summary>
+        public ApiEntries(ApiAction[] actions, bool replace = false) : this(null, actions, replace) { }
 
-        static ApiApplication Application(Type type, bool requireAttribute)
+        /// <summary>添加入口。</summary>
+        public void Add(IEnumerable<ApiApplication> applications, bool replace = false)
         {
-            if (type == null) return null;
-
-            // 检查类型的属性。
-            if (!type.IsClass) return null;
-            if (type.IsAbstract) return null;
-            if (type.IsGenericType) return null;
-            if (type.GetGenericArguments().NotEmpty()) return null;
-            if (!RuntimeUtility.CanNew(type)) return null;
-
-            // 检查类型的特性。
-            var apis = type.GetCustomAttributes(typeof(ApiAttribute), false);
-            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
-            if (requireAttribute && api == null) return null;
-
-            // 检查基类。
-            if (!RuntimeUtility.IsInherits(type, typeof(ApiController))) return null;
-
-            // Entry
-            var entry = new ApiApplication();
-            entry.Type = type;
-            entry.Attribute = api;
-
-            // Attribute
-            if (api != null)
+            if (applications == null) return;
+            lock (locker)
             {
-                entry.Name = string.IsNullOrEmpty(api.Name) ? type.Name : api.Name;
-                entry.Lower = entry.Name.ToLower();
-                var name = api.Name;
-                if (string.IsNullOrEmpty(name)) name = type.Name;
+                foreach (var app in applications)
+                {
+                    if (app == null) continue;
 
-                entry.Caption = api.Caption;
-                entry.Description = api.Description;
-            }
-            else
-            {
-                entry.Name = type.Name;
-                entry.Lower = entry.Name.ToLower();
-                entry.Caption = null;
-                entry.Description = null;
-                entry.Hidden = true;
-            }
+                    var appKey = app.Name.Lower();
+                    if (appKey.IsEmpty()) continue;
 
-            // Caption
-            if (string.IsNullOrEmpty(entry.Caption))
-            {
-                var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
-                if (captions.Length > 0)
-                {
-                    var caption = (CaptionAttribute)captions[0];
-                    entry.Caption = caption.Title;
-                    entry.Description = caption.Description;
+                    if (_apps.ContainsKey(appKey))
+                    {
+                        if (replace) _apps[appKey] = app;
+                    }
+                    else
+                    {
+                        _apps.Add(appKey, app);
+                    }
                 }
             }
-
-            // Hidden
-            if (type.Contains<HiddenAttribute>(false)) entry.Hidden = true;
-
-            // Independent
-            if (type.Contains<IndependentAttribute>(false)) entry.Independent = true;
-
-            // Module
-            var assemblyName = type.Assembly.GetName();
-            entry.Module = TextUtility.Join("-", assemblyName.Name, assemblyName.Version.ToString());
-
-            return entry;
         }
 
-        static ApiFunction Function(ApiApplication application, MethodInfo method)
+        /// <summary>添加入口。</summary>
+        public void Add(IEnumerable<ApiAction> actions, bool replace = false)
         {
-            if (application == null) return null;
-            if (method == null) return null;
-
-            // 滤除构造函数、抽象方法、泛型和非本类定义方法。
-            if (method.IsConstructor) return null;
-            if (method.IsAbstract) return null;
-            if (method.GetGenericArguments().NotEmpty()) return null;
-
-            // 滤除 get 和 set 访问器。
-            var methodName = method.Name;
-            if (methodName.StartsWith("get_") || methodName.StartsWith("set_")) return null;
-
-            // 定义者。
-            var declaring = method.DeclaringType;
-            if (declaring.Equals(typeof(object))) return null;
-
-            // 检查 ApiAttribute 特性。
-            var apis = method.GetCustomAttributes(typeof(ApiAttribute), false);
-            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
-
-            // Entry
-            var entry = new ApiFunction();
-            entry.Application = application;
-            entry.Method = method;
-
-            // 返回值。
-            var returnable = method.ReturnType;
-            if (returnable.Equals(typeof(void))) returnable = null;
-            entry.Returnable = returnable;
-
-            // 参数。
-            var pis = method.GetParameters();
-            if (pis != null && pis.Length > 0)
+            if (actions == null) return;
+            lock (locker)
             {
-                var pisc = pis.Length;
-                for (var i = 0; i < pisc; i++)
+                foreach (var action in actions)
                 {
-                    var pi = pis[i];
-                    if (pi.IsIn) return null;
-                    if (pi.IsOut) return null;
-                }
-                entry.Parameters = pis;
+                    if (action == null) continue;
 
-                if (pisc == 1)
-                {
-                    var pi = pis[0];
-                    var pt = pi.ParameterType;
-                    if (RuntimeUtility.IsInherits(pt, typeof(Source.Record))) entry.ParamIsRecord = true;
+                    var actionKey = action.Path.Lower();
+                    if (actionKey.IsEmpty()) continue;
+
+                    if (_actions.ContainsKey(actionKey))
+                    {
+                        if (replace) _actions[actionKey] = action;
+                    }
+                    else
+                    {
+                        _actions.Add(actionKey, action);
+                    }
                 }
             }
+        }
 
-            if (api != null)
+        /// <summary>追加指定的集合,指定 replace 参数将替换当前实例中的同名的入口。</summary>
+        public void Add(ApiEntries entries, bool replace = false)
+        {
+            if (entries == null) return;
+            Add(entries.Applications, replace);
+            Add(entries.Actions, replace);
+        }
+
+        /// <summary>清空当前实例。</summary>
+        public void Clear()
+        {
+            lock (locker)
             {
-                entry.Name = string.IsNullOrEmpty(api.Name) ? method.Name : api.Name;
-                entry.Lower = entry.Name.ToLower();
-                entry.Caption = api.Caption;
-                entry.Description = api.Description;
+                _apps.Clear();
+                _actions.Clear();
             }
-            else
+        }
+
+        /// <summary>生成 Json 实例。</summary>
+        public Json ToJson()
+        {
+            lock (locker)
             {
-                entry.Name = method.Name;
-                entry.Lower = entry.Name.ToLower();
-                entry.Caption = null;
-                entry.Description = null;
+                var obj = new
+                {
+                    applications = Applications,
+                    actions = Actions
+                };
+                return Json.From(obj);
             }
-            entry.Name = method.Name;
-            entry.Lower = entry.Name.ToLower();
+        }
+
+        #endregion
+
+        #region static
+
+        /// <summary>从指定的程序集获取入口。</summary>
+        public static ApiEntries From(Assembly assembly, bool replace = false)
+        {
+            if (assembly == null) return null;
 
-            // Caption
-            var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
-            if (captions.Length > 0)
+            var apps = new List<ApiApplication>();
+            var actions = new List<ApiAction>();
+            var types = assembly.GetExportedTypes();
+            foreach (var type in types)
             {
-                var caption = (CaptionAttribute)captions[0];
-                entry.Caption = caption.Title;
-                entry.Description = caption.Description;
+                apps.Add(ApiApplication.Parse(type, true));
+                actions.AddRange(ApiAction.Parse(type));
             }
 
-            // Hidden
-            entry.Hidden = application.Hidden;
-            if (!entry.Hidden && method.Contains<HiddenAttribute>(false)) entry.Hidden = true;
+            var entries = new ApiEntries(apps, actions, replace);
+            return entries;
+        }
 
-            return entry;
+        /// <summary>从多个程序集中获取入口。</summary>
+        public static ApiEntries From(IEnumerable<Assembly> assemblies, bool replace = false)
+        {
+            if (assemblies == null) return null;
+            var entries = new ApiEntries();
+            foreach (var assembly in assemblies) entries.Add(From(assembly), replace);
+            return entries;
         }
 
+        /// <summary>从当前程序中获取入口。 </summary>
+        public static ApiEntries Calling(bool replace = false) => From(Assembly.GetCallingAssembly(), replace);
+
+        /// <summary>从当前 AppDomain 中获取入口。</summary>
+        public static ApiEntries AppDomain(bool replace = false) => From(System.AppDomain.CurrentDomain.GetAssemblies(), replace);
+
         #endregion
 
     }
diff --git a/Apewer/Web/ApiEntry.cs b/Apewer/Web/ApiEntry.cs
deleted file mode 100644
index e065588..0000000
--- a/Apewer/Web/ApiEntry.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Text;
-
-namespace Apewer.Web
-{
-
-    /// <summary>API 入口。</summary>
-    [Serializable]
-    public sealed class ApiEntry : IToJson
-    {
-
-        #region Reflection
-
-        internal Assembly _assembly = null;
-        internal Module _module = null;
-        internal Type _type = null;
-        internal MethodInfo _method = null;
-
-        /// <summary>定义当前入口的程序集。</summary>
-        public Assembly Assembly { get => _assembly; }
-
-        /// <summary>定义当前入口的模块。</summary>
-        public Module Module { get => _module; }
-
-        /// <summary>定义当前入口的类型。</summary>
-        public Type Type { get => _type; }
-
-        /// <summary>定义当前入口的方法。</summary>
-        public MethodInfo Method { get => _method; }
-
-        #endregion
-
-        #region Define
-
-        internal CaptionAttribute _caption = null;
-        internal HiddenAttribute _hidden = null;
-
-        internal RoutePrefixAttribute _prefix = null;
-        internal RouteAttribute _route = null;
-        internal string _path = null;
-        internal string _lower = null;
-
-        /// <summary>当前入口的路由前缀。</summary>
-        public RoutePrefixAttribute RoutePrefix { get => _prefix; }
-
-        /// <summary>当前入口的路由。</summary>
-        public RouteAttribute Route { get => _route; }
-
-        /// <summary>当前入口的标题。</summary>
-        public CaptionAttribute Caption { get => _caption; }
-
-        /// <summary>当前入口的路由路径。</summary>
-        public string Path { get => _path; }
-
-        #endregion
-
-        #region HTTP Method
-
-        internal bool _restricted = false;
-        internal HttpConnectAttribute _connect = null;
-        internal HttpDeleteAttribute _delete = null;
-        internal HttpGetAttribute _get = null;
-        internal HttpHeadAttribute _head = null;
-        internal HttpOptionsAttribute _options = null;
-        internal HttpPatchAttribute _patch = null;
-        internal HttpPostAttribute _post = null;
-        internal HttpPutAttribute _put = null;
-        internal HttpTraceAttribute _trace = null;
-
-        /// <summary>当前入口拥有 <see cref="HttpConnectAttribute"/> 特性。</summary>
-        public HttpConnectAttribute Connect { get => _connect; }
-
-        /// <summary>当前入口拥有 <see cref="HttpDeleteAttribute"/> 特性。</summary>
-        public HttpDeleteAttribute Delete { get => _delete; }
-
-        /// <summary>当前入口拥有 <see cref="HttpGetAttribute"/> 特性。</summary>
-        public HttpGetAttribute Get { get => _get; }
-
-        /// <summary>当前入口拥有 <see cref="HttpHeadAttribute"/> 特性。</summary>
-        public HttpHeadAttribute Head { get => _head; }
-
-        /// <summary>当前入口拥有 <see cref="HttpOptionsAttribute"/> 特性。</summary>
-        public HttpOptionsAttribute Options { get => _options; }
-
-        /// <summary>当前入口拥有 <see cref="HttpPatchAttribute"/> 特性。</summary>
-        public HttpPatchAttribute Patch { get => _patch; }
-
-        /// <summary>当前入口拥有 <see cref="HttpPostAttribute"/> 特性。</summary>
-        public HttpPostAttribute Post { get => _post; }
-
-        /// <summary>当前入口拥有 <see cref="HttpPutAttribute"/> 特性。</summary>
-        public HttpPutAttribute Put { get => _put; }
-
-        /// <summary>当前入口拥有 <see cref="HttpTraceAttribute"/> 特性。</summary>
-        public HttpTraceAttribute Trace { get => _trace; }
-
-        #endregion
-
-        private ApiEntry() { }
-
-        internal static ApiEntry[] Parse(Type type)
-        {
-            return null;
-        }
-
-        internal static ApiEntry Parse(Type type, MethodInfo method, RoutePrefixAttribute prefix, bool typeIndependent)
-        {
-            if (type == null) return null;
-            if (method == null) return null;
-            if (!method.IsPublic) return null;
-            if (method.IsGenericMethod) return null;
-            if (method.IsStatic) return null;
-
-            var route = RuntimeUtility.GetAttribute<RouteAttribute>(method, false);
-            var path = route == null ? null : Concat(prefix, route);
-            var lower = path == null ? null : path.ToLower();
-
-            var entry = new ApiEntry();
-
-            entry._assembly = type.Assembly;
-            entry._module = type.Module;
-            entry._type = type;
-            entry._method = method;
-
-            entry._caption = RuntimeUtility.GetAttribute<CaptionAttribute>(method, true);
-            entry._hidden = RuntimeUtility.GetAttribute<HiddenAttribute>(method, true);
-
-            entry._prefix = prefix;
-            entry._route = route;
-            entry._path = path;
-            entry._lower = lower;
-
-            // 允许的 HTTP 方法,指定任何方法即表示需要限定方法。
-            entry._connect = RuntimeUtility.GetAttribute<HttpConnectAttribute>(method, false);
-            entry._delete = RuntimeUtility.GetAttribute<HttpDeleteAttribute>(method, false);
-            entry._get = RuntimeUtility.GetAttribute<HttpGetAttribute>(method, false);
-            entry._head = RuntimeUtility.GetAttribute<HttpHeadAttribute>(method, false);
-            entry._options = RuntimeUtility.GetAttribute<HttpOptionsAttribute>(method, false);
-            entry._patch = RuntimeUtility.GetAttribute<HttpPatchAttribute>(method, false);
-            entry._post = RuntimeUtility.GetAttribute<HttpPostAttribute>(method, false);
-            entry._put = RuntimeUtility.GetAttribute<HttpPutAttribute>(method, false);
-            entry._trace = RuntimeUtility.GetAttribute<HttpTraceAttribute>(method, false);
-            if (entry._get != null) entry._restricted = true;
-            else if (entry._post != null) entry._restricted = true;
-            else if (entry._options != null) entry._restricted = true;
-            else if (entry._connect != null) entry._restricted = true;
-            else if (entry._delete != null) entry._restricted = true;
-            else if (entry._head != null) entry._restricted = true;
-            else if (entry._patch != null) entry._restricted = true;
-            else if (entry._put != null) entry._restricted = true;
-            else if (entry._trace != null) entry._restricted = true;
-
-            return entry;
-        }
-
-        static string Concat(RoutePrefixAttribute prefix, RouteAttribute route)
-        {
-            var segs = new List<string>(16);
-
-            if (prefix != null && !string.IsNullOrEmpty(prefix.Path))
-            {
-                var split = prefix.Path.Split('/');
-                var count = split.Length;
-                for (var i = 0; i < count; i++)
-                {
-                    var seg = split[i];
-                    if (string.IsNullOrEmpty(seg)) continue;
-                    segs.Add(seg);
-                }
-            }
-
-            if (route != null && !string.IsNullOrEmpty(route.Path))
-            {
-                var split = route.Path.Split('/');
-                var count = split.Length;
-                for (var i = 0; i < count; i++)
-                {
-                    var seg = split[i];
-                    if (string.IsNullOrEmpty(seg)) continue;
-                    segs.Add(seg);
-                }
-            }
-
-            if (segs.Count < 1) return "/";
-            return "/" + TextUtility.Join("/", segs.ToArray());
-        }
-
-        /// <summary>生成包含当前实例属性的 Json 对象。</summary>
-        /// <returns>Json 对象。</returns>
-        public Json ToJson()
-        {
-            var json = Json.NewObject();
-            return json;
-        }
-
-    }
-
-}
diff --git a/Apewer/Web/ApiFunction.cs b/Apewer/Web/ApiFunction.cs
index bfd5849..885ca94 100644
--- a/Apewer/Web/ApiFunction.cs
+++ b/Apewer/Web/ApiFunction.cs
@@ -5,61 +5,157 @@ using System.Reflection;
 namespace Apewer.Web
 {
 
-    internal sealed class ApiFunction
+    /// <summary></summary>
+    public sealed class ApiFunction : IToJson
     {
 
-        internal ApiApplication Application;
-        internal MethodInfo Method;
-        internal Type Returnable;
-        internal ParameterInfo[] Parameters;
-        internal bool ParamIsRecord = false;
+        #region fields
 
-        // 主特性和主要属性。
-        // internal ApiAttribute Attribute;
-        internal string Name = null;
-        internal string Lower = null;
+        ApiApplication _application = null;
+        MethodInfo _method = null;
+        Type _return = null;
+        ApiParameter[] _parameters = null;
 
-        // 附加特性。
-        internal bool Hidden;
-        internal string Caption;
-        internal string Description;
+        string _name = null;
+        string _lower = null;
+        string _caption = null;
+        string _description = null;
 
-        private Class<Json> psJson = null;
+        bool _hidden = false;
 
-        internal Json ToJson(ApiOptions options)
+        #endregion
+
+        #region properties
+
+        /// <summary></summary>
+        public ApiApplication Application { get => _application; }
+
+        /// <summary>方法。</summary>
+        public MethodInfo Method { get => _method; }
+
+        /// <summary>参数。</summary>
+        public ApiParameter[] Parameters { get => _parameters.Map(x => x); }
+
+        /// <summary>返回的类型。</summary>
+        public Type ReturnType { get => _return; }
+
+        /// <summary></summary>
+        public string Name { get => _name; }
+
+        /// <summary></summary>
+        public string Caption { get => _caption; }
+
+        /// <summary></summary>
+        public string Description { get => _description; }
+
+        /// <summary></summary>
+        public bool Hidden { get => _hidden; }
+
+        #endregion
+
+        /// <summary></summary>
+        public Json ToJson() => ToJson(true);
+
+        /// <summary></summary>
+        public Json ToJson(ApiOptions options) => ToJson((options ?? new ApiOptions()).WithParameters);
+
+        /// <summary></summary>
+        public Json ToJson(bool withParameters)
         {
             if (Hidden) return null;
+
             var json = Json.NewObject();
             json.SetProperty("name", Name);
             if (!string.IsNullOrEmpty(Caption)) json.SetProperty("caption", Caption);
             if (!string.IsNullOrEmpty(Description)) json.SetProperty("description", Description);
-            if (options.WithParameters)
+            if (withParameters) json.SetProperty("parameters", Json.From(_parameters));
+            return json;
+        }
+
+        ApiFunction(MethodInfo method, ApiApplication application, ApiParameter[] parameters)
+        {
+            // 检查 ApiAttribute 特性。
+            var apis = method.GetCustomAttributes(typeof(ApiAttribute), false);
+            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
+
+            // Entry
+            _application = application;
+            _method = method;
+
+            // 返回值。
+            _return = method.ReturnType;
+            if (_return.Equals(typeof(void))) _return = null;
+
+            // api
+            if (api == null)
             {
-                if (psJson == null)
+                _name = method?.Name;
+                _lower = _name?.Lower();
+            }
+            else
+            {
+                _name = string.IsNullOrEmpty(api.Name) ? method?.Name : api.Name;
+                _lower = _name?.Lower();
+                _caption = api.Caption;
+                _description = api.Description;
+            }
+
+            // caption
+            if (string.IsNullOrEmpty(_caption))
+            {
+                var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
+                if (captions.Length > 0)
                 {
-                    if (Parameters == null || Parameters.Length < 0)
-                    {
-                        psJson = new Class<Json>();
-                    }
-                    else
+                    var caption = (CaptionAttribute)captions[0];
+                    _caption = caption.Title;
+                    if (string.IsNullOrEmpty(_description))
                     {
-                        var ps = Json.NewArray();
-                        var psList = new List<ParameterInfo>();
-                        psList.AddRange(Parameters);
-                        psList.Sort(new Comparison<ParameterInfo>((a, b) => a.Name.CompareTo(b.Name)));
-                        foreach (var pi in psList)
-                        {
-                            var p = Json.NewObject();
-                            p.SetProperty("name", pi.Name);
-                            p.SetProperty("type", pi.ParameterType.Name);
-                            ps.AddItem(p);
-                        }
-                        psJson = new Class<Json>(ps);
+                        _description = caption.Description;
                     }
                 }
-                if (psJson) json.SetProperty("parameters", psJson.Value);
             }
-            return json;
+
+            // hidden
+            if (method.Contains<HiddenAttribute>(false)) _hidden = true;
+
+            // 参数。
+            _parameters = parameters;
+        }
+
+        /// <summary></summary>
+        public static ApiFunction Parse(ApiApplication application, MethodInfo method)
+        {
+            if (application == null) return null;
+            if (method == null) return null;
+
+            // 滤除构造函数、抽象方法、泛型和非本类定义方法。
+            if (method.IsConstructor) return null;
+            if (method.IsAbstract) return null;
+            if (method.GetGenericArguments().NotEmpty()) return null;
+
+            // 滤除 get 和 set 访问器。
+            var methodName = method.Name;
+            if (methodName.StartsWith("get_") || methodName.StartsWith("set_")) return null;
+            switch (methodName)
+            {
+                case "Dispose":
+                    return null;
+            }
+
+            // 定义者。
+            var declaring = method.DeclaringType;
+            if (declaring.Equals(typeof(object))) return null;
+
+            // 参数,所有参数必须是 In 方向。
+            var ps = new List<ApiParameter>();
+            foreach (var pi in method.GetParameters())
+            {
+                var p = ApiParameter.Parse(pi);
+                if (p == null) return null;
+                ps.Add(p);
+            }
+
+            return new ApiFunction(method, application, ps.ToArray());
         }
 
     }
diff --git a/Apewer/Web/ApiInvoker.cs b/Apewer/Web/ApiInvoker.cs
index 50c1cdf..b357223 100644
--- a/Apewer/Web/ApiInvoker.cs
+++ b/Apewer/Web/ApiInvoker.cs
@@ -51,23 +51,27 @@ namespace Apewer.Web
         }
 
         /// <summary>发起调用。</summary>
-        public string Invoke(ApiProvider provider)
+        /// <exception cref="ArgumentNullException" />
+        public void Invoke(ApiProvider provider)
         {
+            if (provider == null) throw new ArgumentNullException(nameof(provider));
+
             var entries = Entries;
             if (entries == null) entries = ApiEntries.AppDomain();
             Entries = entries;
-            return Invoke(provider, entries);
+            Invoke(provider, entries);
         }
 
         /// <summary>发起调用。</summary>
-        public string Invoke(ApiProvider provider, ApiEntries entries)
+        /// <exception cref="ArgumentNullException" />
+        public void Invoke(ApiProvider provider, ApiEntries entries)
         {
-            if (provider == null) return "未指定有效的服务程序。";
-            if (entries == null) return "未指定有效的入口。";
+            if (provider == null) throw new ArgumentNullException(nameof(provider));
+            if (entries == null) throw new ArgumentNullException(nameof(entries));
 
             var context = new ApiContext(this, provider, entries);
             var processor = new ApiProcessor(context);
-            return processor.Run();
+            processor.Run();
         }
 
     }
diff --git a/Apewer/Web/ApiMiddleware.cs b/Apewer/Web/ApiMiddleware.cs
deleted file mode 100644
index f79fc27..0000000
--- a/Apewer/Web/ApiMiddleware.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-#if Middleware
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Apewer.Web
-{
-
-    /// <summary>中间件。</summary>
-    public abstract class ApiMiddleware
-    {
-
-    }
-
-}
-
-#endif
diff --git a/Apewer/Web/ApiModel.cs b/Apewer/Web/ApiModel.cs
index 40b7813..ae3e16a 100644
--- a/Apewer/Web/ApiModel.cs
+++ b/Apewer/Web/ApiModel.cs
@@ -8,19 +8,15 @@ namespace Apewer.Web
 {
 
     /// <summary>Response 模型。</summary>
-    public abstract class ApiModel : IToJson
+    public abstract class ApiModel : IApiModel
     {
 
-        #region
+        #region Headers
 
-        private int _expires = 0;
+        int _expires = 0;
+        StringPairs _headers = new StringPairs();
 
-        internal ApiRequest _request;
-        internal ApiResponse _response;
-        internal ApiOptions _options;
-        internal ApiProvider _provider;
-
-        static int SafeExpires(int seconds)
+        int SafeExpires(int seconds)
         {
             var s = seconds;
             if (s < 0) s = 0;
@@ -28,38 +24,37 @@ namespace Apewer.Web
             return s;
         }
 
-        #endregion
+        /// <summary>响应缓存的过期时间,以秒为单位。</summary>
+        public virtual int Expires { get => _expires; set => _expires = SafeExpires(value); }
 
-        #region 内部属性和方法。
+        /// <summary>状态。</summary>
+        /// <remarks>默认值:200。</remarks>
+        public virtual int Status { get; set; }
 
-        /// <summary>处理当前模型的 API 请求。</summary>
-        protected ApiRequest Request { get => _request; }
+        /// <summary>设置 Response 头。</summary>
+        public virtual StringPairs Headers { get => _headers; set => _headers = value ?? new StringPairs(); }
 
-        /// <summary>处理当前模型的 API 响应。</summary>
-        protected ApiResponse Response { get => Response; }
+        /// <summary>内容类型。当 Headers 中包含 Content-Type 时此属性将被忽略。</summary>
+        public virtual string ContentType { get; set; }
 
-        /// <summary>处理当前模型的 API 选项。</summary>
-        protected ApiOptions Options { get => _options; }
+        /// <summary>设置文件名,告知客户端此附件处理此响应。</summary>
+        public virtual string Attachment { get; set; }
 
-        /// <summary>处理当前模型的服务程序实例。</summary>
-        protected ApiProvider Provider { get => _provider; }
+        #endregion
 
-        /// <summary>在 Response 头中添加用于设置文件名的属性。</summary>
-        protected void SetAttachment()
-        {
-            if (_provider == null) return;
-            var name = Attachment;
-            if (string.IsNullOrEmpty(name)) return;
-            var encoded = TextUtility.EncodeUrl(name);
-            _provider.SetHeader("Content-Disposition", $"attachment; filename={encoded}");
-        }
+        #region Output
 
-        private List<string> WriteHeader()
-        {
-            if (_provider == null) return null;
+        /// <summary>执行输出。</summary>
+        /// <remarks>此方法由 API 调用器发起调用,用户程序不应主动调用。</remarks>
+        /// <exception cref="InvalidOperationException"></exception>
+        public abstract void Output(ApiContext context);
 
+        /// <summary>向 HTTP 写入头。</summary>
+        /// <returns>已写入的头。</returns>
+        List<string> WriteHeaders(ApiContext context)
+        {
             var status = Status > 0 ? Status : 200;
-            if (status != 200) _provider.SetStatus(status);
+            if (status != 200) context.Provider.SetStatus(status);
 
             var headers = Headers;
             var added = new List<string>(32);
@@ -69,69 +64,82 @@ namespace Apewer.Web
                 {
                     if (header.Key.IsEmpty()) continue;
                     if (header.Value.IsEmpty()) continue;
-                    _provider.SetHeader(header.Key, header.Value);
+                    context.Provider.SetHeader(header.Key, header.Value);
                     added.Add(header.Key.Lower());
                 }
             }
 
-            SetAttachment();
-            _provider.SetCache(Expires);
-            if (!added.Contains("content-type")) _provider.SetContentType(ContentType);
+            SetAttachment(context);
+            context.Provider.SetCache(Expires);
+            if (!added.Contains("content-type")) context.Provider.SetContentType(ContentType);
             return added;
         }
 
-        /// <summary>以指定参数输出。</summary>
-        protected void Output(byte[] bytes)
+        /// <summary>在 Response 头中添加用于设置文件名的属性。</summary>
+        void SetAttachment(ApiContext context)
         {
-            if (_provider == null) return;
-            if (_provider.PreWrite().NotEmpty()) return;
-            var added = WriteHeader();
+            var name = Attachment;
+            if (string.IsNullOrEmpty(name)) return;
+            var encoded = TextUtility.EncodeUrl(name);
+            context.Provider.SetHeader("Content-Disposition", $"attachment; filename={encoded}");
+        }
 
+        /// <summary>输出头和响应体,响应体是字节数组。</summary>
+        /// <exception cref="ArgumentNullException" />
+        protected void Output(ApiContext context, byte[] bytes)
+        {
+            if (context == null) throw new ArgumentNullException(nameof(context));
+
+            // 写入头
+            if (context.Provider == null) return;
+            if (context.Provider.PreWrite().NotEmpty()) return;
+            var added = WriteHeaders(context);
+
+            // 写入头
             var length = bytes == null ? 0 : bytes.Length;
-            if (!added.Contains("content-length")) _provider.SetContentLength(length);
-            if (length > 0) _provider.ResponseBody().Write(bytes);
-            _provider.Sent();
+            if (!added.Contains("content-length")) context.Provider.SetContentLength(length);
+
+            // 写入主体
+            if (length > 0) context.Provider.ResponseBody().Write(bytes);
+
+            // 发送
+            context.Provider.Sent();
         }
 
         /// <summary>以指定参数输出。</summary>
-        protected void Output(Stream stream, bool dispose)
+        /// <exception cref="ArgumentNullException" />
+        protected void Output(ApiContext context, Stream stream)
         {
-            if (_provider == null) return;
-            if (_provider.PreWrite().NotEmpty()) return;
-            var added = WriteHeader();
+            if (context == null) throw new ArgumentNullException(nameof(context));
 
-            if (!added.Contains("content-length"))
+            // 写入头
+            if (context.Provider == null) return;
+            if (context.Provider.PreWrite().NotEmpty()) return;
+            var added = WriteHeaders(context);
+
+            if (stream == null)
             {
-                var length = stream.Length - stream.Position;
-                _provider.SetContentLength(length);
+                context.Provider.SetContentLength(0);
+                context.Provider.Sent();
             }
-            _provider.ResponseBody().Write(stream);
-            _provider.Sent();
-            if (dispose) RuntimeUtility.Dispose(stream);
-        }
-
-        #endregion
-
-        /// <summary>状态。</summary>
-        /// <remarks>默认值:200。</remarks>
-        public virtual int Status { get; set; }
-
-        /// <summary>内容类型。当 Headers 中包含 Content-Type 时此属性将被忽略。</summary>
-        public virtual string ContentType { get; set; }
-
-        /// <summary>响应缓存的过期时间,以秒为单位。</summary>
-        public virtual int Expires { get => _expires; set => _expires = SafeExpires(value); }
+            else
+            {
+                // 写入头
+                if (!added.Contains("content-length"))
+                {
+                    var length = stream.Length - stream.Position;
+                    context.Provider.SetContentLength(length);
+                }
 
-        /// <summary>设置文件名,告知客户端此附件处理此响应。</summary>
-        public virtual string Attachment { get; set; }
+                // 写入主体
+                context.Provider.ResponseBody().Write(stream);
 
-        /// <summary>设置 Response 头。</summary>
-        public virtual StringPairs Headers { get; set; }
+                // 发送
+                context.Provider.Sent();
+            }
+        }
 
-        /// <summary>执行输出。</summary>
-        /// <remarks>此方法由 API 调用器发起调用,用户程序不应主动调用。</remarks>
-        /// <exception cref="InvalidOperationException"></exception>
-        public abstract void Output();
+        #endregion
 
         /// <summary>创建对象实例,并设置默认属性。</summary>
         public ApiModel()
@@ -143,15 +151,6 @@ namespace Apewer.Web
             Headers = new StringPairs();
         }
 
-        /// <summary></summary>
-        public Json ToJson()
-        {
-            var json = new Json();
-            json.SetProperty("status", Status);
-            json.SetProperty("content-type", ContentType);
-            return json;
-        }
-
     }
 
     /// <summary>输出二进制的 Response 模型。</summary>
@@ -162,7 +161,7 @@ namespace Apewer.Web
         public byte[] Bytes { get; set; }
 
         /// <summary>输出字节数组。</summary>
-        public override void Output() => Output(Bytes);
+        public override void Output(ApiContext context) => Output(context, Bytes);
 
         /// <summary>创建对象实例,并设置默认属性。</summary>
         public ApiBytesModel(byte[] bytes = null, string contentType = "application/octet-stream")
@@ -185,7 +184,17 @@ namespace Apewer.Web
         public bool AutoDispose { get; set; }
 
         /// <summary>输出流。</summary>
-        public override void Output() => Output(Stream, AutoDispose);
+        public override void Output(ApiContext context)
+        {
+            if (AutoDispose)
+            {
+                using (var stream = Stream) Output(context, Stream);
+            }
+            else
+            {
+                Output(context, Stream);
+            }
+        }
 
         /// <summary>当指定 AutoDispose 属性时释放流。</summary>
         public void Dispose()
@@ -220,21 +229,17 @@ namespace Apewer.Web
         public string Path { get => _path; set => SetPath(value); }
 
         /// <summary>输出指定路径的文件。</summary>
-        public override void Output()
+        public override void Output(ApiContext context)
         {
-            try
-            {
-                if (!File.Exists(Path)) return;
+            if (!File.Exists(Path)) return;
 
-                var info = new FileInfo(Path);
-                if (string.IsNullOrEmpty(Attachment)) Attachment = info.Name;
+            var info = new FileInfo(Path);
+            if (string.IsNullOrEmpty(Attachment)) Attachment = info.Name;
 
-                using (var stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read))
-                {
-                    Output(stream, false);
-                }
+            using (var stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read))
+            {
+                Output(context, stream);
             }
-            catch { }
         }
 
         /// <summary></summary>
@@ -254,7 +259,7 @@ namespace Apewer.Web
         public string Text { get; set; }
 
         /// <summary>输出文本。</summary>
-        public override void Output() => Output(TextUtility.Bytes(Text));
+        public override void Output(ApiContext context) => Output(context, TextUtility.Bytes(Text));
 
         /// <summary>创建对象实例,并设置默认属性。</summary>
         public ApiTextModel(string text = null, string contentType = "text/plain")
@@ -281,15 +286,15 @@ namespace Apewer.Web
         public bool Camel { get; set; }
 
         /// <summary>输出文本。</summary>
-        public override void Output()
+        public override void Output(ApiContext context)
         {
             var json = (Json != null && Json.Available) ? Json : Json.NewObject();
             if (Camel) Json.Camel(json);
-            Output(TextUtility.Bytes(json.ToString(Indented)));
+            Output(context, TextUtility.Bytes(json.ToString(Indented)));
         }
 
         /// <summary>创建对象实例,并设置默认属性。</summary>
-        public ApiJsonModel(Json json = null, bool camel = false, bool indented = true)
+        public ApiJsonModel(Json json = null, bool indented = false, bool camel = true)
         {
             ContentType = "application/json";
             Camel = camel;
@@ -304,20 +309,21 @@ namespace Apewer.Web
     {
 
         /// <summary>将要重定向的位置。</summary>
-        public string Location { get; set; }
+        public string Location { get; private set; }
 
         /// <summary>执行重定向。</summary>
-        public override void Output()
+        public override void Output(ApiContext context)
         {
             var location = Location;
             if (string.IsNullOrEmpty(location)) return;
-            if (Provider == null) return;
-            Provider.SetRedirect(Location);
+            context.Provider.SetRedirect(Location);
         }
 
         /// <summary>重定向到指定的 URL。</summary>
-        public ApiRedirectModel(string location = null)
+        /// <exception cref="ArgumentNullException" />
+        public ApiRedirectModel(string location)
         {
+            if (location.IsEmpty()) throw new ArgumentNullException(nameof(location));
             Location = location;
         }
 
@@ -331,23 +337,24 @@ namespace Apewer.Web
         public Exception Exception { get; set; }
 
         /// <summary>解析 Exception 的内容并输出。</summary>
-        public override void Output()
+        public override void Output(ApiContext context)
         {
             Status = 500;
             ContentType = "text/plain";
-            Output(ToString().Bytes());
+            Output(context, Format(Exception).Bytes());
         }
 
         /// <summary></summary>
-        public ApiExceptionModel(Exception exception = null)
+        /// <exception cref="ArgumentNullException" />
+        public ApiExceptionModel(Exception exception)
         {
+            if (exception == null) throw new ArgumentNullException(nameof(exception));
             Exception = exception;
         }
 
         /// <summary></summary>
-        public override string ToString()
+        static string Format(Exception ex)
         {
-            var ex = Exception;
             var sb = new StringBuilder();
             if (ex == null)
             {
@@ -357,7 +364,7 @@ namespace Apewer.Web
             {
                 try
                 {
-                    sb.Append(Exception.GetType().FullName);
+                    sb.Append(ex.GetType().FullName);
 
                     var props = ex.GetType().GetProperties();
                     foreach (var prop in props)
@@ -399,7 +406,7 @@ namespace Apewer.Web
         public byte[] Bytes { get; set; }
 
         /// <summary>执行重定向。</summary>
-        public override void Output() => Output(Bytes);
+        public override void Output(ApiContext context) => Output(context, Bytes);
 
         /// <summary></summary>
         public ApiStatusModel(int status = 200) => Status = status;
diff --git a/Apewer/Web/ApiOptions.cs b/Apewer/Web/ApiOptions.cs
index d1a3811..6208d50 100644
--- a/Apewer/Web/ApiOptions.cs
+++ b/Apewer/Web/ApiOptions.cs
@@ -38,6 +38,10 @@ namespace Apewer.Web
         // /// </remarks>
         // public bool AllowSynchronousIO { get; set; } = true;
 
+        /// <summary>默认的结果渲染器。</summary>
+        /// <remarks>默认值:NULL</remarks>
+        public Action<ApiContext, object> DefaultRenderer { get; set; }
+
         /// <summary>允许输出的 Json 对象缩进。</summary>
         /// <remarks>默认值:不缩进。</remarks>
         public bool JsonIndent { get; set; } = false;
@@ -49,10 +53,22 @@ namespace Apewer.Web
         /// <summary>输出前的检查。</summary>
         public ApiPreOutput PreOutput { get; set; }
 
+        /// <summary>默认的结果渲染器。</summary>
+        /// <remarks>默认值:<see cref="ApiUtility.Error(ApiContext, string)"/></remarks>
+        public Action<ApiContext, string> TextRenderer { get; set; } = ApiUtility.Error;
+
         /// <summary>在响应头中设置 Content-Security-Policy,要求浏览器升级资源链接,使用 HTTPS。</summary>
         /// <remarks>默认值:不要求。在 HTTPS 页面中,不自动升级 HTTP 资源。</remarks>
         public bool UpgradeHttps { get; set; } = false;
 
+        /// <summary>使用反射。</summary>
+        /// <remarks>默认值:使用</remarks>
+        public bool UseReflection { get; set; } = true;
+
+        /// <summary>使用路由。</summary>
+        /// <remarks>默认值:使用</remarks>
+        public bool UseRoute { get; set; } = true;
+
         /// <summary>在响应中包含 Access-Control 属性。</summary>
         /// <remarks>默认值:不包含。</remarks>
         public bool WithAccessControl { get; set; } = false;
diff --git a/Apewer/Web/ApiParameter.cs b/Apewer/Web/ApiParameter.cs
new file mode 100644
index 0000000..8944391
--- /dev/null
+++ b/Apewer/Web/ApiParameter.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>API 行为的参数。</summary>
+    public sealed class ApiParameter : IToJson
+    {
+
+        ParameterInfo _parameter = null;
+        bool _body = false;
+        bool _query = false;
+
+        /// <summary></summary>
+        public ParameterInfo ParameterInfo { get => _parameter; }
+
+        /// <summary></summary>
+        public string Name { get => _parameter.Name; }
+
+        /// <summary></summary>
+        public Type Type { get => _parameter.ParameterType; }
+
+        /// <summary></summary>
+        public bool FromBody { get => _body; }
+
+        /// <summary></summary>
+        public bool FromQuery { get => _query; }
+
+        /// <summary></summary>
+        public override string ToString() => _parameter.Name;
+
+        /// <summary></summary>
+        public Json ToJson()
+        {
+            var format = ApiActionJsonFormat.Default ?? new ApiActionJsonFormat();
+            var withReflection = format.WithReflection;
+            return ToJson(withReflection);
+        }
+
+        /// <summary></summary>
+        public Json ToJson(bool withReflection)
+        {
+            var json = Json.NewObject();
+            json.SetProperty("name", _parameter.Name);
+            if (withReflection)
+            {
+                json.SetProperty("type", _parameter.ParameterType.Name);
+            }
+            if (_body) json.SetProperty("fromBody", _body);
+            if (_query) json.SetProperty("fromQuery", _query);
+            return json;
+        }
+
+        /// <summary></summary>
+        /// <exception cref="ArgumentNullException" />
+        ApiParameter(ParameterInfo parameterInfo)
+        {
+            _parameter = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
+            _query = RuntimeUtility.Contains<FromQueryAttribute>(parameterInfo);
+            _body = RuntimeUtility.Contains<FromBodyAttribute>(parameterInfo);
+        }
+
+        /// <summary></summary>
+        public static ApiParameter Parse(ParameterInfo parameter)
+        {
+            if (parameter == null) return null;
+            if (parameter.IsOut) return null;
+            if (parameter.IsRetval) return null;
+            return new ApiParameter(parameter);
+        }
+
+    }
+
+}
diff --git a/Apewer/Web/ApiProcessor.cs b/Apewer/Web/ApiProcessor.cs
index 01f6034..97567a3 100644
--- a/Apewer/Web/ApiProcessor.cs
+++ b/Apewer/Web/ApiProcessor.cs
@@ -2,7 +2,7 @@
 using Apewer.Source;
 using System;
 using System.Net;
-
+using System.Reflection;
 using static Apewer.Web.ApiUtility;
 
 namespace Apewer.Web
@@ -11,67 +11,64 @@ namespace Apewer.Web
     internal class ApiProcessor
     {
 
-        // in
         private ApiContext _context = null;
 
-        // temp
-        // private Uri _url = null;
-        // private HttpMethod _method = HttpMethod.NULL;
-
-        // out
-        // private ApiEntry _entry = null;
-        // private ApiRequest _request = null;
-        // private ApiResponse _response = null;
-        // private ApiController _controller = null;
+        internal ApiProcessor(ApiContext context) => _context = context ?? throw new ArgumentNullException(nameof(context));
 
-        internal ApiProcessor(ApiContext context)
-        {
-            if (context == null) throw new ArgumentNullException(nameof(context));
-            _context = context;
-        }
+        #region prepare
 
         /// <summary>执行处理程序,返回错误信息。</summary>
-        public string Run()
+        public void Run()
         {
-            var error = Flow();
-            return error;
-        }
+            var url = null as Uri;
+            var method = HttpMethod.NULL;
+            var response = null as ApiResponse;
 
-        string Flow()
-        {
             try
             {
                 // 检查执行的前提条件,获取 Method 和 URL。
-                Uri url = null;
-                HttpMethod method = HttpMethod.NULL;
                 var check = Check(ref method, ref url);
-                if (!string.IsNullOrEmpty(check)) return check;
+                if (!string.IsNullOrEmpty(check))
+                {
+                    Logger.Internals.Error(typeof(ApiInvoker), check);
+                    return;
+                }
 
                 // 准备请求模型。
                 var request = GetRequest(_context.Provider, _context.Options, method, url);
                 _context.Request = request;
 
                 // 准备响应模型。
-                var response = new ApiResponse();
+                response = new ApiResponse();
                 response.Random = request.Random;
                 response.Application = request.Application;
                 response.Function = request.Function;
                 _context.Response = response;
 
                 // 调用 API。
-                var invoke = Invoke();
-                if (!string.IsNullOrEmpty(invoke)) return invoke;
-
-                // 输出。
-                response.Duration = Duration(_context.Beginning);
-                Output(_context.Provider, _context.Options, response, request, method);
-                return null;
+                Invoke();
             }
             catch (Exception ex)
             {
                 var message = ex.Message();
                 Logger.Internals.Error(typeof(ApiInvoker), message);
-                return 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);
+                    }
+                }
             }
         }
 
@@ -79,18 +76,10 @@ namespace Apewer.Web
         {
             var span = DateTime.Now - beginning;
             var ms = span.TotalMilliseconds;
-            if (ms > 0D)
-            {
-                var s = span.TotalMilliseconds / 1000D;
-                if (s > 10D) return Math.Round(s, 1).ToString() + "s";
-                if (s > 1D) return Math.Round(s, 2).ToString() + "s";
-                if (ms > 10D) return Math.Round(ms, 0).ToString() + "ms";
-                return Math.Round(ms, 1).ToString() + "ms";
-            }
-            else
-            {
-                return null;
-            }
+            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)
@@ -133,18 +122,171 @@ namespace Apewer.Web
         }
 
         // 寻找入口。
-        string Invoke()
+        void Invoke()
         {
-            var appName = _context.Request.Application;
-            var application = _context.Entries.Get(appName);
-            Invoke(application);
+            // 路由
+            if (_context.Options.UseRoute)
+            {
+                var path = _context?.Request?.Url?.AbsolutePath;
+                var action = _context.Entries.GetAction(path);
+                if (action != null)
+                {
+                    Invoke(action);
+                    _context.Response.Duration = Duration(_context.Beginning);
+                    return;
+                }
+            }
 
-            var duration = Duration(_context.Beginning);
-            _context.Response.Duration = duration;
+            // 反射
+            if (_context.Options.UseReflection)
+            {
+                var appName = _context.Request.Application;
+                var application = _context.Entries.GetApplication(appName);
+                Invoke(application);
+                _context.Response.Duration = Duration(_context.Beginning);
+                return;
+            }
 
-            return null;
+            // 未匹配到
+            _context.Response.Duration = Duration(_context.Beginning);
+            _context.Response.Model = new ApiStatusModel(404);
+        }
+
+        #endregion
+
+        #region common
+
+        // 创建控制器实例
+        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;
+        }
+
+        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) 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)
+            {
+                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)
+            {
+                response.Data = toJson.ToJson();
+                return;
+            }
+
+            // 未知返回类型,尝试使用默认渲染器。
+            var defaultRenderer = context.Options.DefaultRenderer;
+            if (defaultRenderer != null) defaultRenderer.Invoke(context, returnValue);
+        }
+
+        #endregion
+
+        #region route
+
+        // 执行 Action。
+        void Invoke(ApiAction action)
+        {
+            var controller = null as ApiController;
+            try
+            {
+                // 准备控制器。
+                controller = CreateController(action.Type, _context.Request, _context.Response, _context.Options);
+
+                // 准备参数。
+                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 Invoke(ApiApplication application)
         {
@@ -153,9 +295,6 @@ namespace Apewer.Web
             var request = _context.Request;
             var response = _context.Response;
 
-            var function = null as ApiFunction;
-            var controller = null as ApiController;
-
             // Application 无效,尝试默认控制器和枚举。
             if (application == null)
             {
@@ -163,40 +302,54 @@ namespace Apewer.Web
                 if (@default == null)
                 {
                     // 没有指定默认控制器,尝试枚举。
-                    response.Error("Invalid Application");
-                    if (options.AllowEnumerate) response.Data = Enumerate(entries.Enumerate(), options);
+                    response.Status = "notfound";
+                    response.Message = "Not Found";
+                    if (options.AllowEnumerate) response.Data = Enumerate(entries.Applications, options);
                     return;
                 }
                 else
                 {
                     // 创建默认控制器。
-                    try { controller = CreateController(@default, request, response, options); }
-                    catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
+                    var controller = null as ApiController;
+                    try
+                    {
+                        controller = CreateController(@default, request, response, options);
+                        Invoke(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。
-                function = application.Get(request.Function);
-                try { controller = CreateController(application.Type, request, response, options); }
-                catch (Exception ex) { ApiUtility.Exception(response, ex.InnerException); }
+                var function = application.GetFunction(request.Function);
+                var controller = null as ApiController;
+                try
+                {
+                    controller = CreateController(application.Type, request, response, options);
+                    Invoke(controller, application, function, options, request, response);
+                }
+                catch (Exception ex)
+                {
+                    ApiUtility.Exception(response, ex.InnerException ?? ex);
+                }
+                finally
+                {
+                    RuntimeUtility.Dispose(controller);
+                }
             }
-            if (controller == null) response.Error("创建控制器实例失败。");
-            else Invoke(controller, application, function, options, request, response);
-            RuntimeUtility.Dispose(controller);
         }
 
         // 调用 Function。
         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
             {
                 // 控制器初始化。
@@ -208,67 +361,8 @@ namespace Apewer.Web
                 if (function != null)
                 {
                     // 调用 API,获取返回值。
-                    var result = function.Method.Invoke(controller, ReadParameters(request, function));
-                    if (response.StopReturn) return;
-
-                    // 检查返回值。
-                    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;
-                    }
-
-                    // 未知返回类型,无法明确输出格式,忽略。
+                    _context.Controller = controller;
+                    Invoke(_context, function.Method, function.Parameters);
                 }
                 else
                 {
@@ -281,44 +375,37 @@ namespace Apewer.Web
                     }
 
                     // 没有执行任何 Function,尝试枚举。
+                    response.Status = "notfound";
                     if (application.Hidden)
                     {
-                        response.Error("Invalid Application");
+                        response.Message = "Not Found";
                     }
                     else
                     {
-                        response.Error("Invalid Function");
-                        if (options.AllowEnumerate) response.Data = Enumerate(application.Items, options);
+                        response.Message = "Not Found";
+                        if (options.AllowEnumerate) response.Data = Enumerate(application.Functions, options);
                     }
                 }
             }
-            catch (Exception exception)
+            catch (Exception ex)
             {
-                var ex = exception.InnerException;
+                if (ex.InnerException != null) ex = ex.InnerException;
+                ApiUtility.Exception(_context.Response, ex, _context.Options.WithException);
 
                 var catcher = _context.Invoker.Catcher;
                 if (catcher != null)
                 {
-                    ApiUtility.Exception(response, ex, false);
                     try
                     {
-                        var apiCatch = new ApiCatch(controller, options, ex);
+                        var apiCatch = new ApiCatch(_context, ex);
                         catcher.Invoke(apiCatch);
                     }
                     catch { }
-                    return;
                 }
-
-                ApiUtility.Exception(response, ex);
             }
         }
 
-        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;
-        }
+        #endregion
 
         #region static
 
@@ -332,7 +419,7 @@ namespace Apewer.Web
 
             // 基本信息。
             var ip = provider.GetClientIP();
-            var headers = provider.GetHeaders() ?? new StringPairs();
+            var headers = provider.GetHeaders() ?? new HttpHeaders();
             request.Headers = headers;
             request.IP = ip;
             request.Url = url;
@@ -352,58 +439,62 @@ namespace Apewer.Web
             var page = null as string;
 
             // 解析 POST 请求。
-            if (request.Method == HttpMethod.POST)
+            switch (request.Method)
             {
-                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)
+                case HttpMethod.PATCH:
+                case HttpMethod.POST:
+                case HttpMethod.PUT:
+                    var preRead = provider.PreRead();
+                    if (string.IsNullOrEmpty(preRead))
                     {
-                        request.PostData = post;
-                        if (length < 104857600)
+                        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
                         {
-                            var text = TextUtility.FromBytes(post);
-                            request.PostText = text;
+                            length = provider.GetContentLength();
+                            if (length <= max) post = provider.RequestBody().Read();
+                        }
 
-                            // 尝试解析 Json,首尾必须是“{}”或“[]”。
-                            var first = post[0];
-                            var last = post[length - 1];
-                            if ((first == 123 && last == 125) || (first == 91 && last == 93))
+                        length = post == null ? 0 : post.Length;
+                        if (length > 1)
+                        {
+                            request.PostData = post;
+                            if (length < 104857600)
                             {
-                                var json = Json.From(text);
-                                if (json != null && json.IsObject)
+                                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))
                                 {
-                                    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();
+                                    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", true) ?? "";
-                            if (contentType.Contains("urlencoded")) request.Form = ApiUtility.Parameters(text);
+                                // 尝试解析 Form,需要 application/x-www-form-urlencoded
+                                var contentType = headers.GetValue("Content-Type") ?? "";
+                                if (contentType.Contains("urlencoded")) request.Form = ApiUtility.Parameters(text);
+                            }
                         }
                     }
-                }
+                    break;
             }
 
             // 解析 URL 参数。
@@ -501,7 +592,7 @@ namespace Apewer.Web
                 {
                     foreach (var header in headers)
                     {
-                        var key = TextUtility.Trim(header.Key);
+                        var key = TextUtility.Trim(header.Name);
                         if (string.IsNullOrEmpty(key)) continue;
                         var value = header.Value;
                         if (string.IsNullOrEmpty(value)) continue;
@@ -512,8 +603,6 @@ namespace Apewer.Web
             return merged;
         }
 
-
-
         internal void Output(ApiProvider provider, ApiOptions options, ApiResponse response, string type, byte[] bytes)
         {
             var preWrite = provider.PreWrite();
@@ -587,15 +676,36 @@ namespace Apewer.Web
             // 设置头。
             var headers = PrepareHeaders(options, response, request);
             foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
-            var model = response.Model;
+
+            // 自定义模型
+            var model = response.Model as IApiModel;
+            var result = response.Model as IActionResult;
             if (model != null)
             {
-                ApiUtility.Initialize(model, request, response, options, provider);
-                try { model.Output(); }
-                catch (Exception ex) { Logger.Internals.Exception(model, ex); }
+                try
+                {
+                    model.Output(_context);
+                }
+                catch (Exception ex)
+                {
+                    Logger.Internals.Exception(model, ex);
+                }
                 RuntimeUtility.Dispose(model);
                 return;
             }
+            else if (result != null)
+            {
+                try
+                {
+                    result.ExecuteResult(_context);
+                }
+                catch (Exception ex)
+                {
+                    Logger.Internals.Exception(result, ex);
+                }
+                RuntimeUtility.Dispose(result);
+                return;
+            }
 
             var text = ApiUtility.ToJson(response, options);
             var bytes = TextUtility.Bytes(text);
diff --git a/Apewer/Web/ApiProvider.cs b/Apewer/Web/ApiProvider.cs
index 452277b..c58e5b1 100644
--- a/Apewer/Web/ApiProvider.cs
+++ b/Apewer/Web/ApiProvider.cs
@@ -51,7 +51,7 @@ namespace Apewer.Web
         public abstract string GetReferrer();
 
         /// <summary>获取请求的头。</summary>
-        public abstract StringPairs GetHeaders();
+        public abstract HttpHeaders GetHeaders();
 
         /// <summary>获取请求的内容类型。</summary>
         public abstract string GetContentType();
@@ -91,4 +91,13 @@ namespace Apewer.Web
 
     }
 
+    /// <summary>API 服务程序。</summary>
+    public abstract class ApiProvider<TContext> : ApiProvider
+    {
+
+        /// <summary>HttpContext</summary>
+        public abstract TContext Context { get; }
+
+    }
+
 }
diff --git a/Apewer/Web/ApiRequest.cs b/Apewer/Web/ApiRequest.cs
index e12e151..8fcbc3c 100644
--- a/Apewer/Web/ApiRequest.cs
+++ b/Apewer/Web/ApiRequest.cs
@@ -14,6 +14,9 @@ namespace Apewer.Web
         private Json _data = null;
         internal string[] _segmentals = null;
 
+        /// <summary>自定义标签。</summary>
+        public object Tag { get; set; }
+
         #region http
 
         /// <summary>客户端 IP 地址。</summary>
@@ -35,7 +38,7 @@ namespace Apewer.Web
         public StringPairs Parameters { get; set; } = new StringPairs();
 
         /// <summary>HTTP 头。</summary>
-        public StringPairs Headers { get; set; } = new StringPairs();
+        public HttpHeaders Headers { get; set; } = new HttpHeaders();
 
         /// <summary>Cookies。</summary>
         public CookieCollection Cookies { get; set; } = new CookieCollection();
diff --git a/Apewer/Web/ApiResponse.cs b/Apewer/Web/ApiResponse.cs
index 7c8af58..2bd0bdd 100644
--- a/Apewer/Web/ApiResponse.cs
+++ b/Apewer/Web/ApiResponse.cs
@@ -1,4 +1,5 @@
 using Apewer.Models;
+using Apewer.Network;
 using System;
 using System.IO;
 using System.Net;
@@ -11,9 +12,12 @@ namespace Apewer.Web
     public sealed class ApiResponse
     {
 
+        /// <summary>自定义标签。</summary>
+        public object Tag { get; set; }
+
         #region internal
 
-        private ApiModel _model = null;
+        private object _model = null;
         private Json _data = Json.NewObject();
 
         internal bool StopReturn = false;
@@ -35,20 +39,19 @@ namespace Apewer.Web
         #region user
 
         /// <summary>头。</summary>
-        public StringPairs Headers { get; set; } = new StringPairs();
+        public HttpHeaders Headers { get; set; } = new HttpHeaders();
 
         /// <summary>Cookies。</summary>
         public CookieCollection Cookies { get; set; } = new CookieCollection();
 
         /// <summary>获取或设置输出模型。</summary>
-        public ApiModel Model
+        public object Model
         {
             get { return _model; }
             set
             {
-                var old = _model;
+                RuntimeUtility.Dispose(_model);
                 _model = value;
-                RuntimeUtility.Dispose(old);
             }
         }
 
diff --git a/Apewer/Web/ApiServiceDescriptor.cs b/Apewer/Web/ApiServiceDescriptor.cs
deleted file mode 100644
index d092d6d..0000000
--- a/Apewer/Web/ApiServiceDescriptor.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-#if Middleware
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Apewer.Web
-{
-
-    /// <summary>API 服务描述。</summary>
-    public sealed class ApiServiceDescriptor : IToJson
-    {
-
-        /// <summary>订阅器的生命周期。</summary>
-        public ApiServiceLifetime Lifetime { get; private set; }
-
-        /// <summary>服务的类型。</summary>
-        public Type ServiceType { get; private set; }
-
-        /// <summary>实现服务的类型。</summary>
-        public Type ImplementationType { get; private set; }
-
-        public object ImplementationInstance
-        {
-            get;
-        }
-
-        public Func<IServiceProvider, object> ImplementationFactory
-        {
-            get;
-        }
-
-        /// <summary>创建订阅器的实例。</summary>
-        /// <param name="invoker">API 调用器。</param>
-        /// <param name="lifetime">订阅器的生命周期。</param>
-        /// <param name="service">服务的类型。</param>
-        /// <param name="implementation">实现服务的类型。</param>
-        /// <exception cref="ArgumentNullException" />
-        /// <exception cref="ArgumentException" />
-        internal ApiServiceDescriptor(ApiServiceLifetime lifetime, Type service, Type implementation)
-        {
-            if (invoker == null) throw new ArgumentNullException(nameof(invoker));
-            if (service == null) throw new ArgumentNullException(nameof(service));
-            if (implementation == null) throw new ArgumentNullException(nameof(implementation));
-
-            if (!service.IsAssignableFrom(implementation)) throw new ArgumentException($"类型 {implementation.Name} 未实现服务。");
-            if (!implementation.IsClass) throw new ArgumentException($"实现服务的类型 {implementation.Name} 不是引用类型。");
-            if (implementation.IsAbstract) throw new ArgumentException($"实现服务的类型 {implementation.Name} 是抽象类型,无法实例化。");
-
-            Lifetime = lifetime;
-            ServiceType = service;
-            ImplementationType = implementation;
-        }
-
-        /// <summary>创建订阅器的实例。</summary>
-        /// <param name="invoker">API 调用器。</param>
-        /// <param name="lifetime">订阅器的生命周期。</param>
-        /// <param name="service">服务的类型。</param>
-        /// <param name="implementation">实现服务的方法。</param>
-        /// <exception cref="ArgumentNullException" />
-        /// <exception cref="ArgumentException" />
-        internal ApiServiceDescriptor(ApiServiceLifetime lifetime, Type service, Func<Type,  implementation)
-        {
-            if (invoker == null) throw new ArgumentNullException(nameof(invoker));
-            if (service == null) throw new ArgumentNullException(nameof(service));
-            if (implementation == null) throw new ArgumentNullException(nameof(implementation));
-
-            if (!service.IsAssignableFrom(implementation)) throw new ArgumentException($"类型 {implementation.Name} 未实现服务。");
-            if (!implementation.IsClass) throw new ArgumentException($"实现服务的类型 {implementation.Name} 不是引用类型。");
-            if (implementation.IsAbstract) throw new ArgumentException($"实现服务的类型 {implementation.Name} 是抽象类型,无法实例化。");
-
-            Invoker = invoker;
-            Lifetime = lifetime;
-            ServiceType = service;
-            ImplementationType = implementation;
-        }
-
-        /// <summary>生成 JSON 实例。</summary>
-        public Json ToJson()
-        {
-            var json = new Json();
-            json["Lifetime"] = Lifetime.ToString();
-            json["Service"] = ServiceType.FullName;
-            json["Implementation"] = Implementation.FullName;
-            return json;
-        }
-
-    }
-
-}
-
-#endif
diff --git a/Apewer/Web/ApiServiceLifetime.cs b/Apewer/Web/ApiServiceLifetime.cs
deleted file mode 100644
index 358e699..0000000
--- a/Apewer/Web/ApiServiceLifetime.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Apewer.Web
-{
-
-    /// <summary>服务的生命周期。</summary>
-    public enum ApiServiceLifetime
-    {
-
-        /// <summary>对所有请求使用同一个实例。</summary>
-        Singleton,
-
-        /// <summary>对每个请求使用同一个实例。</summary>
-        Scoped,
-
-        /// <summary>对每个请求里的每次声明创建新实例。</summary>
-        Transient
-
-    }
-
-}
diff --git a/Apewer/Web/ApiUtility.cs b/Apewer/Web/ApiUtility.cs
index 41de0e8..0e56a23 100644
--- a/Apewer/Web/ApiUtility.cs
+++ b/Apewer/Web/ApiUtility.cs
@@ -117,7 +117,7 @@ namespace Apewer.Web
         }
 
         /// <summary>获取 User Agent。</summary>
-        public static string UserAgent(StringPairs headers) => headers == null ? null : headers.GetValue("user-agent");
+        public static string UserAgent(HttpHeaders headers) => headers == null ? null : headers.GetValue("user-agent");
 
         // 从 Uri 对象中解析路径片段。
         private static string[] Segmentals(Uri url)
@@ -162,11 +162,11 @@ namespace Apewer.Web
         }
 
         /// <summary>获取 X-Forwarded-For,不存在时返回 NULL 值。</summary>
-        public static string[] GetForwardedIP(StringPairs headers)
+        public static string[] GetForwardedIP(HttpHeaders headers)
         {
             if (headers != null)
             {
-                var value = headers.GetValue("x-forwarded-for", true);
+                var value = headers.GetValue("x-forwarded-for");
                 if (!string.IsNullOrEmpty(value))
                 {
                     var fips = new List<string>();
@@ -349,12 +349,12 @@ namespace Apewer.Web
             }
         }
 
-        internal static CookieCollection ParseCookies(StringPairs headers)
+        internal static CookieCollection ParseCookies(HttpHeaders headers)
         {
             var cookies = new CookieCollection();
             if (headers == null) return cookies;
 
-            var hvs = headers.GetValues("cookie", true, false);
+            var hvs = headers.GetValues("Cookie");
             foreach (var hv in hvs)
             {
                 if (string.IsNullOrEmpty(hv)) continue;
@@ -534,6 +534,25 @@ namespace Apewer.Web
 
         #endregion
 
+        #region ApiContext
+
+        /// <summary>作为纯文本输出。</summary>
+        /// <remarks>Content-Type: text/plain</remarks>
+        public static void Text(ApiContext context, string text)
+        {
+            if (context == null) return;
+            Model(context.Response, new ApiTextModel(text, "text/plain"));
+        }
+
+        /// <summary>设置 status 为 error,并设置 message 的内容。</summary>
+        public static void Error(ApiContext context, string text)
+        {
+            if (context == null) return;
+            Error(context.Response, text);
+        }
+
+        #endregion
+
         #region ApiRequest
 
         /// <summary>获取 URL 路径段,不存在的段为 NULL 值。可要求解码。</summary>
@@ -707,22 +726,24 @@ namespace Apewer.Web
             if (response == null) return;
             response.Model = null;
             response.Status = "exception";
-            if (exception == null) return;
 
-            try
+            if (exception != null)
             {
-                if (setData)
+                try
                 {
-                    var json = ToJson(exception);
-                    response.Message = json["message"];
-                    response.Data = json;
-                }
-                else
-                {
-                    response.Message = exception.Message();
+                    if (setData)
+                    {
+                        var json = ToJson(exception);
+                        response.Message = json["message"];
+                        response.Data = json;
+                    }
+                    else
+                    {
+                        response.Message = exception.Message();
+                    }
                 }
+                catch { }
             }
-            catch { }
         }
 
         private static Json ToJson(Exception exception, bool withInner = true)
@@ -810,23 +831,24 @@ namespace Apewer.Web
 
         #endregion
 
-        #region ApiModel
+        #region ApiResult
 
-        /// <summary>初始化 ApiMode 的属性。</summary>
-        public static void Initialize(ApiModel model, ApiRequest request, ApiResponse response, ApiOptions options, ApiProvider provider)
+        /// <summary>对 HTTP 结果设置文件名。</summary>
+        /// <param name="result">结果。</param>
+        /// <param name="name">文件名(未编码)。</param>
+        public static void SetAttachemnt(this HeadResult result, string name)
         {
-            if (model == null) return;
-            model._request = request;
-            model._response = response;
-            model._options = options;
-            model._provider = provider;
+            if (result == null) throw new ArgumentNullException(nameof(result));
+            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+            var encoded = TextUtility.EncodeUrl(name);
+            result.Headers.Add("Content-Disposition", $"attachment; filename={encoded}");
         }
 
         #endregion
 
         #region ApiFunction Parameters
 
-
         internal static object[] ReadParameters(ApiRequest request, ApiFunction function)
         {
             if (request == null || function == null) return null;
@@ -840,31 +862,41 @@ namespace Apewer.Web
         public static object[] ReadParameters(ApiRequest request, ParameterInfo[] parameters)
         {
             if (request == null || parameters == null || parameters.Length < 1) return null;
+            var apiParameters = parameters.Map(x => ApiParameter.Parse(x) ?? throw new Exception($"参数【{x.Name}】无效。"));
+            return ReadParameters(request, apiParameters);
+        }
 
-            var pis = parameters;
-            if (pis == null) return null;
+        /// <summary>为带有形参的 Function 准备实参。</summary>
+        /// <param name="request">API 请求模型。</param>
+        /// <param name="parameters">Function 的参数信息。</param>
+        /// <returns>实参。</returns>
+        public static object[] ReadParameters(ApiRequest request, ApiParameter[] parameters)
+        {
+            if (request == null || parameters == null || parameters.Length < 1) return null;
+
+            if (parameters == null) return null;
 
-            var count = pis.Length;
+            var count = parameters.Length;
             if (count < 1) return null;
 
             // 当 Function 仅有一个参数时,尝试生成模型。
-            if (count == 1)
+            if (count == 1 && parameters[0] != null)
             {
-                var pin = pis[0].Name;
-                var pit = pis[0].ParameterType;
+                var parameterName = parameters[0].Name;
+                var parameterType = parameters[0].Type;
 
                 // POST
                 if (request.Method == HttpMethod.POST)
                 {
                     // string
-                    if (pit.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(pin, true) };
+                    if (parameterType.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(parameterName, true) };
 
                     // json
-                    if (pit.Equals(typeof(Json))) return new object[] { request.PostJson };
+                    if (parameterType.Equals(typeof(Json))) return new object[] { request.PostJson };
 
 #if !NET20
                     // dynamic
-                    if (pit.Equals(typeof(object)))
+                    if (parameterType.Equals(typeof(object)))
                     {
                         try
                         {
@@ -902,15 +934,13 @@ namespace Apewer.Web
 #endif
 
                     // class
-                    if (pit.IsClass)
+                    if (parameterType.IsClass)
                     {
                         try
                         {
-                            var entity = Activator.CreateInstance(pit);
-                            var setted = false;
-                            if (!setted) setted = ReadParameter(request.Data, entity);
-                            if (!setted) setted = ReadParameter(request.PostJson, entity);
-                            return new object[] { entity };
+                            var entity = ReadParameter(request.Data, parameterType);
+                            if (entity == null) entity = ReadParameter(request.PostJson, parameterType);
+                            if (entity != null) return new object[] { entity.Value };
                         }
                         catch { }
                     }
@@ -923,42 +953,51 @@ namespace Apewer.Web
                 else
                 {
                     // string
-                    if (pit.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(pin, true) };
+                    if (parameterType.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(parameterName, true) };
 
                     // json
-                    if (pit.Equals(typeof(Json))) return new object[] { Json.From(request.Parameters.GetValue(pin, true)) };
+                    if (parameterType.Equals(typeof(Json))) return new object[] { Json.From(request.Parameters.GetValue(parameterName, true)) };
                 }
             }
 
-            var ps = new object[count];
+            var values = new object[count];
             for (var i = 0; i < count; i++)
             {
-                var name = pis[i].Name;
-                var type = pis[i].ParameterType;
-                var text = Parameter(request, name);
-                ps[i] = ReadParameter(text, type);
+                if (parameters[i] != null)
+                {
+                    var name = parameters[i].Name;
+                    var type = parameters[i].Type;
+                    var text = Parameter(request, name);
+                    values[i] = ReadParameter(text, type);
+                }
             }
-            return ps;
+            return values;
         }
 
-        static bool ReadParameter(Json json, object entity)
+        static Class<object> ReadParameter(Json json, Type type)
         {
-            if (!json) return false;
-            if (json.IsObject)
-            {
-                var properties = json.GetProperties();
-                if (properties.Length < 1) return false;
-                Json.Object(entity, json, true, null, true);
-                return true;
-            }
-            if (json.IsArray)
+            if (json)
             {
-                var items = json.GetItems();
-                if (items.Length < 1) return false;
-                Json.Object(entity, json, true, null, true);
-                return true;
+                if (json.IsObject)
+                {
+                    var properties = json.GetProperties();
+                    if (properties.Length > 0)
+                    {
+                        var entity = Json.Object(type, json, true, null, true);
+                        return new Class<object>(entity);
+                    }
+                }
+                if (json.IsArray)
+                {
+                    var items = json.GetItems();
+                    if (items.Length > 0)
+                    {
+                        var entity = Json.Object(type, json, true, null, true);
+                        return new Class<object>(entity);
+                    }
+                }
             }
-            return false;
+            return null;
         }
 
         static object ReadParameter(string text, Type type)
@@ -1004,7 +1043,7 @@ namespace Apewer.Web
             }
             var json = Json.NewObject();
             json.SetProperty("count", count);
-            json.SetProperty("list", list);
+            json.SetProperty("applications", list);
             return json;
         }
 
@@ -1024,7 +1063,7 @@ namespace Apewer.Web
             }
             var json = Json.NewObject();
             json.SetProperty("count", count);
-            json.SetProperty("list", list);
+            json.SetProperty("functions", list);
             return json;
         }
 
diff --git a/Apewer/Web/BytesResult.cs b/Apewer/Web/BytesResult.cs
new file mode 100644
index 0000000..52378eb
--- /dev/null
+++ b/Apewer/Web/BytesResult.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>表示 API 行为结果,主体为字节数组。</summary>
+    public class BytesResult : HeadResult
+    {
+
+        #region content
+
+        /// <summary>主体。</summary>
+        public virtual byte[] Bytes { get; set; }
+
+        /// <summary>创建结果实例。</summary>
+        public BytesResult(byte[] bytes, string contentType = "application/octet-stream") : this(200, bytes, contentType) { }
+
+        /// <summary>创建结果实例。</summary>
+        public BytesResult(int status, byte[] bytes, string contentType = "application/octet-stream") : base(status)
+        {
+            Headers.Add("Content-Type", contentType);
+            Bytes = bytes;
+        }
+
+        #endregion
+
+        #region execute
+
+        /// <summary>写入主体。</summary>
+        /// <param name="context">API 上下文。</param>
+        /// <param name="bodyData">主体的内容,字节数应该和 Content-Length 一致。</param>
+        protected virtual void WriteBody(ApiContext context, byte[] bodyData)
+        {
+            if (bodyData != null && bodyData.Length > 0)
+            {
+                var stream = context.Provider.ResponseBody();
+                stream.Write(bodyData, 0, bodyData.Length);
+            }
+        }
+
+        /// <summary>写入 HTTP 头和主体。</summary>
+        public override void ExecuteResult(ApiContext context)
+        {
+            if (Bytes == null)
+            {
+                WriteHead(context, 0);
+            }
+            else
+            {
+                WriteHead(context, Bytes.Length);
+                if (Bytes.Length > 0L)
+                {
+                    var stream = context.Provider.ResponseBody();
+                    stream.Write(Bytes);
+                }
+            }
+        }
+
+        #endregion
+
+    }
+
+}
diff --git a/Apewer/Web/CacheControl.cs b/Apewer/Web/CacheControl.cs
new file mode 100644
index 0000000..17fa652
--- /dev/null
+++ b/Apewer/Web/CacheControl.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>缓存控制的值。</summary>
+    public static class CacheControl
+    {
+
+        /// <summary>不缓存。</summary>
+        public const string Disabled = "no-cache, no-store, must-revalidate";
+
+        /// <summary>1 天的秒数。</summary>
+        public const int Day = 86400;
+
+        /// <summary>30 天的秒数。</summary>
+        public const int Month = 259200;
+
+        /// <summary>365 天的秒数。</summary>
+        public const int Year = 31536000;
+
+        /// <summary>构建缓存指令,此指令允许所有缓存。</summary>
+        /// <param name="maxAge">浏览器的缓存秒数。</param>
+        /// <param name="sMaxAge">代理服务器的缓存秒数。</param>
+        public static string Public(int maxAge, int sMaxAge)
+        {
+            if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
+            if (sMaxAge < 0) throw new ArgumentOutOfRangeException(nameof(sMaxAge));
+            return $"public, max-age={maxAge}, s-maxage={sMaxAge}, must-revalidate";
+        }
+
+        /// <summary>构建缓存指令,此指令仅允许浏览器缓存。</summary>
+        /// <param name="maxAge">浏览器的缓存秒数。</param>
+        public static string Private(int maxAge)
+        {
+            if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
+            return $"private, max-age={maxAge}, must-revalidate";
+        }
+
+    }
+
+}
diff --git a/Apewer/Web/FromBodyAttribute.cs b/Apewer/Web/FromBodyAttribute.cs
new file mode 100644
index 0000000..70ba568
--- /dev/null
+++ b/Apewer/Web/FromBodyAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+    public sealed class FromBodyAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/FromFormAttribute.cs b/Apewer/Web/FromFormAttribute.cs
new file mode 100644
index 0000000..ea6c3be
--- /dev/null
+++ b/Apewer/Web/FromFormAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+    public sealed class FromFormAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/FromHeaderAttribute.cs b/Apewer/Web/FromHeaderAttribute.cs
new file mode 100644
index 0000000..8069744
--- /dev/null
+++ b/Apewer/Web/FromHeaderAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+    public sealed class FromHeaderAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/FromQueryAttribute.cs b/Apewer/Web/FromQueryAttribute.cs
new file mode 100644
index 0000000..2a098bd
--- /dev/null
+++ b/Apewer/Web/FromQueryAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
+    public sealed class FromQueryAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HeadResult.cs b/Apewer/Web/HeadResult.cs
new file mode 100644
index 0000000..49592ab
--- /dev/null
+++ b/Apewer/Web/HeadResult.cs
@@ -0,0 +1,92 @@
+using Apewer.Network;
+using System;
+
+namespace Apewer.Web
+{
+
+    /// <summary>表示 API 行为结果,仅包含头。</summary>
+    public class HeadResult : ActionResult, IActionResult, IHttpActionResult
+    {
+
+        #region content
+
+        int _status = 200;
+        HttpHeaders _headers = new HttpHeaders();
+
+        /// <summary>由 RFC 7231 定义的状态码。</summary>
+        /// <value>1xx (Informational)<br />2xx (Successful)<br />3xx (Redirection)<br />4xx (Client Error)<br />5xx (Server Error)</value>
+        public virtual int StatusCode
+        {
+            get { return _status; }
+            set { _status = value; }
+        }
+
+        /// <summary>头部。</summary>
+        public virtual HttpHeaders Headers
+        {
+            get { return _headers; }
+            set { _headers = value ?? new HttpHeaders(); }
+        }
+
+        /// <summary>创建结果实例。</summary>
+        public HeadResult() : this(200) { }
+
+        /// <summary>创建结果实例。</summary>
+        public HeadResult(int status)
+        {
+            StatusCode = status;
+        }
+
+        #endregion
+
+        #region execute
+
+        /// <summary>写入 HTTP 头。</summary>
+        /// <param name="context">API 上下文。</param>
+        /// <param name="contentLength">内容长度。指定为负数时不写入 HTTP 头。</param>
+        protected virtual void WriteHead(ApiContext context, long contentLength)
+        {
+            context.Provider.SetStatus(StatusCode);
+
+            const string ContentType = "Content-Type";
+            const string ContentLength = "Content-Length";
+            foreach (var header in _headers)
+            {
+                if (header.Name.IsEmpty()) continue;
+                if (header.Value.IsEmpty()) continue;
+
+                // Content-Length
+                if (ContentLength.Equals(header.Name, StringComparison.CurrentCultureIgnoreCase))
+                {
+                    continue;
+                }
+
+                // Content-Type
+                if (ContentType.Equals(header.Name, StringComparison.CurrentCultureIgnoreCase))
+                {
+                    context.Provider.SetContentType(header.Value);
+                    continue;
+                }
+
+                // default
+                context.Provider.SetHeader(header.Name, header.Value);
+            }
+
+            // Content-Length
+            if (contentLength >= 0L)
+            {
+                context.Provider.SetContentLength(contentLength);
+            }
+        }
+
+        /// <summary>写入 HTTP 头,其中不包含 Content-Length。</summary>
+        public override void ExecuteResult(ApiContext context)
+        {
+            WriteHead(context, 0L);
+        }
+
+        #endregion
+
+    }
+
+}
diff --git a/Apewer/Web/HttpConnectAttribute.cs b/Apewer/Web/HttpConnectAttribute.cs
new file mode 100644
index 0000000..0188586
--- /dev/null
+++ b/Apewer/Web/HttpConnectAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpConnectAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpDeleteAttribute.cs b/Apewer/Web/HttpDeleteAttribute.cs
new file mode 100644
index 0000000..aa87da9
--- /dev/null
+++ b/Apewer/Web/HttpDeleteAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpDeleteAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpGetAttribute.cs b/Apewer/Web/HttpGetAttribute.cs
new file mode 100644
index 0000000..af24867
--- /dev/null
+++ b/Apewer/Web/HttpGetAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpGetAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpHeadAttribute.cs b/Apewer/Web/HttpHeadAttribute.cs
new file mode 100644
index 0000000..e7a99c0
--- /dev/null
+++ b/Apewer/Web/HttpHeadAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpHeadAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpListenerProvider.cs b/Apewer/Web/HttpListenerProvider.cs
index c53a620..9530734 100644
--- a/Apewer/Web/HttpListenerProvider.cs
+++ b/Apewer/Web/HttpListenerProvider.cs
@@ -1,4 +1,5 @@
-using System;
+using Apewer.Network;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Net;
@@ -7,13 +8,16 @@ namespace Apewer.Web
 {
 
     /// <summary>用于网站的服务程序。</summary>
-    public class HttpListenerProvider : ApiProvider
+    public class HttpListenerProvider : ApiProvider<HttpListenerContext>
     {
 
         private HttpListenerContext context;
         private HttpListenerRequest request;
         private HttpListenerResponse response;
 
+        /// <summary>HttpContext</summary>
+        public override HttpListenerContext Context { get => context; }
+
         /// <summary>创建服务程序实例。</summary>
         /// <exception cref="ArgumentNullException"></exception>
         public HttpListenerProvider(HttpListenerContext context)
@@ -59,7 +63,7 @@ namespace Apewer.Web
         public override string GetReferrer() => request.UrlReferrer == null ? null : request.UrlReferrer.OriginalString;
 
         /// <summary>获取请求的头。</summary>
-        public override StringPairs GetHeaders() => StringPairs.From(request.Headers);
+        public override HttpHeaders GetHeaders() => new HttpHeaders(request.Headers);
 
         /// <summary>获取请求的内容类型。</summary>
         public override string GetContentType() => request.ContentType;
diff --git a/Apewer/Web/HttpOptionsAttribute.cs b/Apewer/Web/HttpOptionsAttribute.cs
new file mode 100644
index 0000000..6903961
--- /dev/null
+++ b/Apewer/Web/HttpOptionsAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpOptionsAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpPatchAttribute.cs b/Apewer/Web/HttpPatchAttribute.cs
new file mode 100644
index 0000000..9fad85e
--- /dev/null
+++ b/Apewer/Web/HttpPatchAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpPatchAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpPostAttribute.cs b/Apewer/Web/HttpPostAttribute.cs
new file mode 100644
index 0000000..c6306bf
--- /dev/null
+++ b/Apewer/Web/HttpPostAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpPostAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpPutAttribute.cs b/Apewer/Web/HttpPutAttribute.cs
new file mode 100644
index 0000000..a03c9f5
--- /dev/null
+++ b/Apewer/Web/HttpPutAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpPutAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/HttpTraceAttribute.cs b/Apewer/Web/HttpTraceAttribute.cs
new file mode 100644
index 0000000..a3302de
--- /dev/null
+++ b/Apewer/Web/HttpTraceAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+    public sealed class HttpTraceAttribute : Attribute { }
+
+}
diff --git a/Apewer/Web/IActionResult.cs b/Apewer/Web/IActionResult.cs
new file mode 100644
index 0000000..34c4076
--- /dev/null
+++ b/Apewer/Web/IActionResult.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>定义如何输出 API 结果。</summary>
+    public interface IActionResult
+    {
+
+        /// <summary>执行结果。</summary>
+        void ExecuteResult(ApiContext context);
+
+    }
+
+}
diff --git a/Apewer/Web/IApiModel.cs b/Apewer/Web/IApiModel.cs
new file mode 100644
index 0000000..c72cbf0
--- /dev/null
+++ b/Apewer/Web/IApiModel.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>定义 API 响应模型。</summary>
+    public interface IApiModel
+    {
+
+        /// <summary>输出。</summary>
+        void Output(ApiContext context);
+
+    }
+
+}
diff --git a/Apewer/Web/IHttpActionResult.cs b/Apewer/Web/IHttpActionResult.cs
new file mode 100644
index 0000000..ef76549
--- /dev/null
+++ b/Apewer/Web/IHttpActionResult.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>定义如何输出 API 结果。</summary>
+    public interface IHttpActionResult : IActionResult { }
+
+}
diff --git a/Apewer/Web/MiniConnection.cs b/Apewer/Web/MiniConnection.cs
index 1ec8124..fffb0a6 100644
--- a/Apewer/Web/MiniConnection.cs
+++ b/Apewer/Web/MiniConnection.cs
@@ -387,12 +387,12 @@ namespace Apewer.Web
             }
 
             // 保持连接。
-            _context.Request.KeepAlive = _context.Request.Http11 && TextUtility.Lower(_context.Request.Headers.GetValue("connection", true)) == "keep-alive";
+            _context.Request.KeepAlive = _context.Request.Http11 && TextUtility.Lower(_context.Request.Headers.GetValue("Connection")) == "keep-alive";
 
             // 启用压缩。
             if (_server.Compression)
             {
-                var headerValue = _context.Request.Headers.GetValue("accept-encoding", true);
+                var headerValue = _context.Request.Headers.GetValue("Accept-Encoding");
                 if (!string.IsNullOrEmpty(headerValue))
                 {
                     var split = headerValue.ToLower().Split(',');
@@ -408,7 +408,7 @@ namespace Apewer.Web
             }
 
             // URL
-            var host = _context.Request.Headers.GetValue("host", true);
+            var host = _context.Request.Headers.GetValue("Host");
             var port = 0;
             var local = LocalEndPoint;
             if (local != null)
@@ -449,7 +449,7 @@ namespace Apewer.Web
 
             var headers = _context.Request.Headers;
             var length = -1L;
-            var value = headers.GetValue("Content-Length", true);
+            var value = headers.GetValue("Content-Length");
             if (!string.IsNullOrEmpty(value))
             {
                 var num = value.Int64();
diff --git a/Apewer/Web/MiniProvider.cs b/Apewer/Web/MiniProvider.cs
index 883bfc0..c270373 100644
--- a/Apewer/Web/MiniProvider.cs
+++ b/Apewer/Web/MiniProvider.cs
@@ -8,7 +8,7 @@ namespace Apewer.Web
 {
 
     /// <summary></summary>
-    public sealed class MiniProvider : ApiProvider
+    public sealed class MiniProvider : ApiProvider<MiniContext>
     {
 
         MiniConnection connection;
@@ -16,6 +16,9 @@ namespace Apewer.Web
         MiniRequest request;
         MiniResponse response;
 
+        /// <summary>HttpContext</summary>
+        public override MiniContext Context { get => context; }
+
         /// <summary>创建服务程序实例。</summary>
         /// <exception cref="ArgumentNullException"></exception>
         public MiniProvider(MiniContext context)
@@ -45,16 +48,16 @@ namespace Apewer.Web
         public override long GetContentLength() => request.ContentLength;
 
         /// <summary></summary>
-        public override string GetContentType() => request.Headers.GetValue("Content-Type", true);
+        public override string GetContentType() => request.Headers.GetValue("Content-Type");
 
         /// <summary></summary>
-        public override StringPairs GetHeaders() => request.Headers;
+        public override HttpHeaders GetHeaders() => request.Headers;
 
         /// <summary></summary>
         public override HttpMethod GetMethod() => NetworkUtility.ParseHttpMethod(request.Method);
 
         /// <summary></summary>
-        public override string GetReferrer() => request.Headers.GetValue("Referrer", true);
+        public override string GetReferrer() => request.Headers.GetValue("Referrer");
 
         /// <summary></summary>
         public override Uri GetUrl() => request.Url;
diff --git a/Apewer/Web/MiniRequest.cs b/Apewer/Web/MiniRequest.cs
index 698038b..e37fa8b 100644
--- a/Apewer/Web/MiniRequest.cs
+++ b/Apewer/Web/MiniRequest.cs
@@ -1,4 +1,5 @@
-using System;
+using Apewer.Network;
+using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Globalization;
@@ -28,10 +29,10 @@ namespace Apewer.Web
 
         #region headers
 
-        StringPairs _headers = new StringPairs();
+        HttpHeaders _headers = new HttpHeaders();
 
         /// <summary>头部。</summary>
-        public StringPairs Headers { get => _headers; }
+        public HttpHeaders Headers { get => _headers; }
 
         /// <summary>统一资源定位。</summary>
         public Uri Url { get; internal set; }
@@ -52,7 +53,7 @@ namespace Apewer.Web
         public bool Gzip { get; internal set; }
 
         /// <summary>内容长度,单位:字节。</summary>
-        public long ContentLength { get => _headers.GetValue("Content-Length", true).Int64(); }
+        public long ContentLength { get => _headers.GetValue("Content-Length").Int64(); }
 
         #endregion
 
diff --git a/Apewer/Web/RouteAttribute.cs b/Apewer/Web/RouteAttribute.cs
new file mode 100644
index 0000000..f190113
--- /dev/null
+++ b/Apewer/Web/RouteAttribute.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
+    public sealed class RouteAttribute : Attribute
+    {
+
+        string _path;
+
+        /// <summary></summary>
+        public string Path { get => _path; }
+
+        /// <summary></summary>
+        public RouteAttribute() { }
+
+        /// <summary></summary>
+        public RouteAttribute(string path)
+        {
+            _path = path;
+        }
+
+    }
+
+}
diff --git a/Apewer/Web/RoutePrefixAttribute.cs b/Apewer/Web/RoutePrefixAttribute.cs
new file mode 100644
index 0000000..a11efa1
--- /dev/null
+++ b/Apewer/Web/RoutePrefixAttribute.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary></summary>
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+    public sealed class RoutePrefixAttribute : Attribute
+    {
+
+        string _path;
+
+        /// <summary></summary>
+        public string Path { get { return _path; } }
+
+        /// <summary></summary>
+        /// <param name="path"></param>
+        public RoutePrefixAttribute(string path) { _path = path; }
+
+    }
+
+}
diff --git a/Apewer/Web/StreamResult.cs b/Apewer/Web/StreamResult.cs
new file mode 100644
index 0000000..3ee8121
--- /dev/null
+++ b/Apewer/Web/StreamResult.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>表示 API 行为结果,主体为流。</summary>
+    public sealed class StreamResult : HeadResult, IDisposable
+    {
+
+        #region content
+
+        /// <summary>主体。</summary>
+        public Stream Stream { get; set; }
+
+        /// <summary>Content-Length 的值,用于限制输出的最大长度。指定为 -1 时不限长度,输出到流的末尾。</summary>
+        /// <value>Default = -1</value>
+        public long Length { get; set; }
+
+        /// <summary>输出后自动释放流。</summary>
+        public bool AutoDispose { get; set; }
+
+        /// <summary>创建结果实例。</summary>
+        /// <param name="stream">要输出的流。</param>
+        /// <param name="contentType">内容类型。</param>
+        /// <param name="autoDispose">输出后自动释放流。</param>
+        public StreamResult(Stream stream, string contentType = "application/octet-stream", bool autoDispose = true) : this(200, stream, contentType, -1, autoDispose) { }
+
+        /// <summary>创建结果实例。</summary>
+        /// <param name="status">HTTP 状态码。</param>
+        /// <param name="stream">要输出的流。</param>
+        /// <param name="contentType">内容类型。</param>
+        /// <param name="length">Content-Length 的值。</param>
+        /// <param name="autoDispose">输出后自动释放流。</param>
+        public StreamResult(int status, Stream stream, string contentType = "application/octet-stream", long length = -1, bool autoDispose = true) : base(status)
+        {
+            Headers.Add("Content-Type", contentType);
+            Stream = stream;
+            Length = length;
+            AutoDispose = autoDispose;
+        }
+
+        #endregion
+
+        #region execute
+
+        /// <summary>释放系统资源。</summary>
+        public override void Dispose()
+        {
+            if (Stream != null)
+            {
+                RuntimeUtility.Dispose(Stream);
+                Stream = null;
+            }
+        }
+
+        /// <summary>写入 HTTP 头和主体。</summary>
+        public override void ExecuteResult(ApiContext context)
+        {
+            try
+            {
+                if (Stream == null || Length == 0L)
+                {
+                    WriteHead(context, 0);
+                }
+                else
+                {
+                    WriteHead(context, Length);
+
+                    var writed = 0L;
+                    var capacity = 4096;
+                    var buffer = new byte[capacity];
+                    var body = context.Provider.ResponseBody();
+
+                    // 限制长度。
+                    if (Length > 0L)
+                    {
+                        var remains = Length;
+                        while (true)
+                        {
+                            var limit = Math.Min((int)remains, capacity);
+                            var read = Stream.Read(buffer, 0, limit);
+                            if (read < 1) break;
+
+                            body.Write(buffer, 0, read);
+                            writed += read;
+                            remains -= read;
+                            if (remains < 1L) break;
+                        }
+                    }
+
+                    // 不限制长度,输出到流的末尾。
+                    else
+                    {
+                        while (true)
+                        {
+                            var read = Stream.Read(buffer, 0, capacity);
+                            if (read < 1) break;
+
+                            body.Write(buffer, 0, read);
+                            writed += read;
+                        }
+                    }
+
+                    context.Provider.End();
+                }
+            }
+            finally
+            {
+                if (AutoDispose) Dispose();
+            }
+        }
+
+        #endregion
+
+    }
+
+}
diff --git a/Apewer/Web/TextResult.cs b/Apewer/Web/TextResult.cs
new file mode 100644
index 0000000..2bbad31
--- /dev/null
+++ b/Apewer/Web/TextResult.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Web
+{
+
+    /// <summary>表示 API 行为结果,主体为文本。</summary>
+    public class TextResult : BytesResult
+    {
+
+        /// <summary>创建结果实例。</summary>
+        public TextResult(string text, string contentType = "text/plain") : base(text.Bytes(), contentType) { }
+
+        /// <summary>创建结果实例。</summary>
+        public TextResult(int status, string text, string contentType = "text/plain") : base(status, text.Bytes(), contentType) { }
+
+    }
+
+}
diff --git a/Apewer/Web/WebsiteProvider.cs b/Apewer/Web/WebsiteProvider.cs
index 69aca6f..33fdaa8 100644
--- a/Apewer/Web/WebsiteProvider.cs
+++ b/Apewer/Web/WebsiteProvider.cs
@@ -1,5 +1,6 @@
 #if NETFX
 
+using Apewer.Network;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -9,13 +10,16 @@ namespace Apewer.Web
 {
 
     /// <summary>用于网站的服务程序。</summary>
-    public class WebsiteProvider : ApiProvider
+    public class WebsiteProvider : ApiProvider<HttpContext>
     {
 
         private HttpContext context;
         private HttpRequest request;
         private HttpResponse response;
 
+        /// <summary>HttpContext</summary>
+        public override HttpContext Context { get => context; }
+
         /// <summary>创建服务程序实例。</summary>
         /// <exception cref="ArgumentNullException"></exception>
         public WebsiteProvider(HttpContext context)
@@ -65,7 +69,7 @@ namespace Apewer.Web
         public override string GetReferrer() => request.UrlReferrer == null ? null : request.UrlReferrer.OriginalString;
 
         /// <summary>获取请求的头。</summary>
-        public override StringPairs GetHeaders() => StringPairs.From(request.Headers);
+        public override HttpHeaders GetHeaders() => new HttpHeaders(request.Headers);
 
         /// <summary>获取请求的内容类型。</summary>
         public override string GetContentType() => request.ContentType;
diff --git a/Apewer/Web/_Attributes.cs b/Apewer/Web/_Attributes.cs
deleted file mode 100644
index 5f5348d..0000000
--- a/Apewer/Web/_Attributes.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Apewer.Web
-{
-
-    #region 路由
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
-    public sealed class RoutePrefixAttribute : Attribute
-    {
-
-        string _path;
-
-        /// <summary></summary>
-        public string Path { get { return _path; } }
-
-        /// <summary></summary>
-        /// <param name="path"></param>
-        public RoutePrefixAttribute(string path) { _path = path; }
-
-    }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
-    public sealed class RouteAttribute : Attribute
-    {
-
-        string _path;
-
-        /// <summary></summary>
-        public string Path { get { return _path; } }
-
-        /// <summary></summary>
-        public RouteAttribute(string path) { _path = path; }
-
-    }
-
-    #endregion
-
-    #region 方法
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpConnectAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpDeleteAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpGetAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpHeadAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpOptionsAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpPatchAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpPostAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpPutAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
-    public sealed class HttpTraceAttribute : Attribute { }
-
-    #endregion
-
-    #region 参数
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
-    public sealed class FromBodyAttribute : Attribute { }
-
-    /// <summary></summary>
-    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
-    public sealed class FromUriAttribute : Attribute { }
-
-    #endregion
-
-}
diff --git a/Apewer/_Extensions.cs b/Apewer/_Extensions.cs
index ee57e71..05c683b 100644
--- a/Apewer/_Extensions.cs
+++ b/Apewer/_Extensions.cs
@@ -20,10 +20,10 @@ using System.Dynamic;
 public static class Extensions
 {
 
-    /// <summary>是 NULL 值。</summary>
+    /// <summary>是 NULL 值,或是 DBNULL 值。</summary>
     public static bool IsNull(this object @this) => @this == null || @this.Equals(DBNull.Value);
 
-    /// <summary>不是 NULL 值。</summary>
+    /// <summary>不是 NULL 值,且不是 DBNULL 值。</summary>
     public static bool NotNull(this object @this) => @this != null && !@this.Equals(DBNull.Value);
 
     /// <summary>是默认值。</summary>
@@ -43,16 +43,6 @@ public static class Extensions
     /// <summary>调用 Set 方法。</summary>
     public static void Set<T>(this PropertyInfo @this, object instance, T value) => RuntimeUtility.InvokeSet<T>(instance, @this, value);
 
-    /// <summary>调用方法。</summary>
-    /// <exception cref="ArgumentException"></exception>
-    /// <exception cref="InvalidOperationException"></exception>
-    /// <exception cref="MethodAccessException"></exception>
-    /// <exception cref="NotSupportedException"></exception>
-    /// <exception cref="TargetException"></exception>
-    /// <exception cref="TargetInvocationException"></exception>
-    /// <exception cref="TargetParameterCountException"></exception>
-    public static object Invoke(this MethodInfo @this, object instace, params object[] parameters) => RuntimeUtility.InvokeMethod(instace, @this, parameters);
-
     /// <summary>判断静态属性。</summary>
     public static bool IsStatic(this PropertyInfo @this) => RuntimeUtility.IsStatic(@this);
 
@@ -239,8 +229,9 @@ public static class Extensions
 
     #region DateTime
 
-    /// <summary>获取毫秒时间戳。</summary>
-    public static long Stamp(this DateTime @this, bool byMilliseconds = true) => ClockUtility.Stamp(@this, byMilliseconds);
+    /// <summary>获取毫秒时间戳。当指定了 <see cref="ClockUtility.CustomToStamp"/> 时将优先使用自定义的方法。</summary>
+    /// <remarks>默认不判断参数的时区,与 <see cref="DateTimeKind.Unspecified"/> 相同。</remarks>
+    public static long Stamp(this DateTime dt) => ClockUtility.ToStamp(dt);
 
     /// <summary>转换为易于阅读的文本。</summary>
     /// <remarks>格式:1970-01-01 00:00:00.000</remarks>
@@ -252,9 +243,10 @@ public static class Extensions
     /// <summary>当前 DateTime 为闰年。</summary>
     public static bool LeapYear(this DateTime @this) => ClockUtility.IsLeapYear(@this);
 
-    /// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
+    /// <summary>解析毫秒时间戳,获取 DateTime 对象。当指定了 <see cref="ClockUtility.CustomFromStamp"/> 时将优先使用自定义的方法。</summary>
+    /// <remarks>默认不判断系统时区,返回的结果是 <see cref="DateTimeKind.Unspecified"/>。</remarks>
     /// <exception cref="ArgumentOutOfRangeException"></exception>
-    public static DateTime DateTime(this long stamp, DateTimeKind kind = DateTimeKind.Unspecified, bool throwException = true) => ClockUtility.FromStamp(stamp, kind, throwException);
+    public static DateTime DateTime(this long stamp) => ClockUtility.FromStamp(stamp);
 
     #region Nullable
 
@@ -265,8 +257,9 @@ public static class Extensions
     /// <summary>转换为紧凑的文本。</summary>
     public static string Compact(this DateTime? @this, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true) => @this == null ? default : ClockUtility.Compact(@this.Value, date, time, seconds, milliseconds);
 
-    /// <summary>获取毫秒时间戳。</summary>
-    public static long Stamp(this DateTime? @this, bool byMilliseconds = true) => @this == null ? default : ClockUtility.Stamp(@this.Value, byMilliseconds);
+    /// <summary>获取毫秒时间戳。当指定了 <see cref="ClockUtility.CustomToStamp"/> 时将优先使用自定义的方法。</summary>
+    /// <remarks>默认不判断参数的时区,与 <see cref="DateTimeKind.Unspecified"/> 相同。</remarks>
+    public static long Stamp(this DateTime? @this) => @this == null ? default : ClockUtility.ToStamp(@this.Value);
 
     /// <summary>转换为易于阅读的文本。</summary>
     /// <remarks>格式:1970-01-01 00:00:00.000</remarks>
@@ -276,7 +269,7 @@ public static class Extensions
     public static string Compact(this Class<DateTime> @this, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true) => @this == null ? default : ClockUtility.Compact(@this.Value, date, time, seconds, milliseconds);
 
     /// <summary>获取毫秒时间戳。</summary>
-    public static long Stamp(this Class<DateTime> @this, bool byMilliseconds = true) => @this == null ? default : ClockUtility.Stamp(@this.Value, byMilliseconds);
+    public static long Stamp(this Class<DateTime> @this) => @this == null ? default : ClockUtility.ToStamp(@this.Value);
 
     #endregion
 
@@ -594,13 +587,13 @@ public static class Extensions
     public static void Text(this ApiResponse @this, string text, string contentType = "text/plain") => ApiUtility.Model(@this, new ApiTextModel(text, contentType));
 
     /// <summary>输出 Json 文本。</summary>
-    public static void Json(this ApiResponse @this, Json json, bool indented = true, bool camel = false) => ApiUtility.Model(@this, new ApiJsonModel(json, camel, indented));
+    public static void Json(this ApiResponse @this, Json json, bool indented = false, bool camel = true) => ApiUtility.Model(@this, new ApiJsonModel(json, indented, camel));
 
     /// <summary>输出文件。</summary>
     public static void File(this ApiResponse @this, string path) => ApiUtility.Model(@this, new ApiFileModel(path));
 
     /// <summary>重定向。</summary>
-    public static void Redirect(this ApiResponse @this, string location) => ApiUtility.Model(@this, new ApiRedirectModel() { Location = location });
+    public static void Redirect(this ApiResponse @this, string location) => ApiUtility.Model(@this, new ApiRedirectModel(location));
 
     #endregion
 
diff --git a/ChangeLog.md b/ChangeLog.md
index c780d3e..886d95d 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,13 @@
 
 ### 最新提交
 
+### 6.8.0
+- 新特性
+  - ClockUtility:增加 CustomToStamp 和 CustomFromStamp,支持自定义时间戳的转换方法;
+  - Json:识别实例类型的 .ctor(Json) 构造函数,由类型自己实现反序列化;
+  - Web:兼容微软 API,支持 Route 特性,支持返回 ActionResult;
+  - WindowsUtility:增加读取进程内存的方法。
+
 ### 6.7.6
 - 新特性
   - CollectionUtility:增加数组的 Push 和 Unshift 方法;