|
|
@ -14,11 +14,28 @@ namespace Apewer.Web |
|
|
|
public StaticController() |
|
|
|
{ |
|
|
|
AllowFunction = false; |
|
|
|
AfterInitialized = Execute; |
|
|
|
AfterInitialized = () => |
|
|
|
{ |
|
|
|
if (Request == null || Request.Url == null) return; |
|
|
|
var path = MapPath(Request.Url.AbsolutePath); |
|
|
|
if (StorageUtility.DirectoryExists(path)) |
|
|
|
{ |
|
|
|
ExecuteDirectory(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (StorageUtility.FileExists(path)) |
|
|
|
{ |
|
|
|
ExecuteFile(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
Respond404(path); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
#region property
|
|
|
|
|
|
|
|
/// <summary>获取此静态站点的目录。</summary>
|
|
|
|
public virtual string GetSiteDirectory() |
|
|
|
protected virtual string GetSiteDirectory() |
|
|
|
{ |
|
|
|
var app = WebUtility.AppDirectory; |
|
|
|
var www = StorageUtility.CombinePath(app, "www"); |
|
|
@ -26,6 +43,34 @@ namespace Apewer.Web |
|
|
|
return www; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>响应 403 状态。</summary>
|
|
|
|
protected virtual void Respond403(string path) => Response.Error("403"); |
|
|
|
|
|
|
|
/// <summary>响应 404 状态。</summary>
|
|
|
|
protected virtual void Respond404(string path) => Response.Error("404"); |
|
|
|
|
|
|
|
/// <summary>从扩展名获取内容类型。</summary>
|
|
|
|
/// <remarks>例:<br/>GetContentType("txt") = "text/plain"</remarks>
|
|
|
|
protected virtual string GetContentType(string extension) => Mime(extension); |
|
|
|
|
|
|
|
/// <summary>按扩展名获取过期时间,单位为秒。默认值为 0(不缓存)。</summary>
|
|
|
|
/// <remarks>例:<br/>GetExpires("txt") = 0</remarks>
|
|
|
|
protected virtual int GetExpires(string extension) => 0; |
|
|
|
|
|
|
|
/// <summary>访问目录时,允许列出子项。默认值:false,不允许。</summary>
|
|
|
|
protected virtual bool AllowListChildren() => false; |
|
|
|
|
|
|
|
string PrivateGetContentType(string extension) |
|
|
|
{ |
|
|
|
var result = GetContentType(extension); |
|
|
|
if (string.IsNullOrEmpty(result)) result = Mime(extension); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region common
|
|
|
|
|
|
|
|
/// <summary>解析 URL 的路径,获取本地路径。</summary>
|
|
|
|
protected string MapPath(string urlPath) |
|
|
|
{ |
|
|
@ -43,24 +88,6 @@ namespace Apewer.Web |
|
|
|
return path; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>处理请求。</summary>
|
|
|
|
public virtual void Execute() |
|
|
|
{ |
|
|
|
if (Request == null || Request.Url == null) return; |
|
|
|
var path = MapPath(Request.Url.AbsolutePath); |
|
|
|
if (StorageUtility.DirectoryExists(path)) |
|
|
|
{ |
|
|
|
ExecuteDirectory(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (StorageUtility.FileExists(path)) |
|
|
|
{ |
|
|
|
ExecuteFile(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
Respond404(path); |
|
|
|
} |
|
|
|
|
|
|
|
void ExecuteFile(string path) |
|
|
|
{ |
|
|
|
if (!File.Exists(path)) |
|
|
@ -78,8 +105,7 @@ namespace Apewer.Web |
|
|
|
if (expires > 0) Response.Expires = expires; |
|
|
|
|
|
|
|
// 按扩展名获取 Content-Type。
|
|
|
|
var type = GetContentType(ext); |
|
|
|
// var name = Path.GetFileName(path);
|
|
|
|
var type = PrivateGetContentType(ext); |
|
|
|
|
|
|
|
// Server Side Includes
|
|
|
|
if (ext == "html" || ext == "htm" || ext == "shtml") |
|
|
@ -101,13 +127,89 @@ namespace Apewer.Web |
|
|
|
Respond404(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var @default = GetDefaultFile(path); |
|
|
|
if (string.IsNullOrEmpty(@default)) Respond403(path); |
|
|
|
else ExecuteFile(@default); |
|
|
|
if (!string.IsNullOrEmpty(@default)) |
|
|
|
{ |
|
|
|
ExecuteFile(@default); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (AllowListChildren()) |
|
|
|
{ |
|
|
|
Response.Data = ListChildren(path); |
|
|
|
} |
|
|
|
Respond403(path); |
|
|
|
} |
|
|
|
|
|
|
|
// Server Side Includes
|
|
|
|
string ReadWithSSI(string path, int recursive = 0) |
|
|
|
{ |
|
|
|
if (recursive > 10) return ""; |
|
|
|
|
|
|
|
var input = StorageUtility.ReadFile(path, true); |
|
|
|
if (input == null || input.LongLength < 1) return ""; |
|
|
|
|
|
|
|
// 尝试以 UTF-8 解码。
|
|
|
|
var html = TextUtility.FromBinary(input); |
|
|
|
if (string.IsNullOrEmpty(html)) return ""; |
|
|
|
|
|
|
|
// 按首尾截取。
|
|
|
|
const string left = "<!--"; |
|
|
|
const string right = "-->"; |
|
|
|
const string head = "#include virtual="; |
|
|
|
var sb = new StringBuilder(); |
|
|
|
var text = html; |
|
|
|
while (true) |
|
|
|
{ |
|
|
|
var offset = text.IndexOf(left); |
|
|
|
if (offset < 0) |
|
|
|
{ |
|
|
|
sb.Append(text); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (offset > 0) |
|
|
|
{ |
|
|
|
sb.Append(text.Substring(0, offset)); |
|
|
|
text = text.Substring(offset + left.Length); |
|
|
|
} |
|
|
|
else text = text.Substring(left.Length); |
|
|
|
var length = text.IndexOf(right); |
|
|
|
if (length < 1) |
|
|
|
{ |
|
|
|
sb.Append(left); |
|
|
|
sb.Append(text); |
|
|
|
break; |
|
|
|
} |
|
|
|
var inner = text.Substring(0, length); |
|
|
|
var temp = inner.SafeTrim(); |
|
|
|
if (temp.StartsWith(head)) |
|
|
|
{ |
|
|
|
temp = temp.Substring(head.Length); |
|
|
|
temp = temp.Replace("\"", ""); |
|
|
|
var subPath = MapPath(temp); |
|
|
|
var subText = ReadWithSSI(subPath, recursive + 1); |
|
|
|
if (subText != null && subText.Length > 0) sb.Append(subText); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
sb.Append(left); |
|
|
|
sb.Append(inner); |
|
|
|
sb.Append(right); |
|
|
|
} |
|
|
|
text = text.Substring(length + right.Length); |
|
|
|
} |
|
|
|
|
|
|
|
var output = sb.ToString(); |
|
|
|
return output; |
|
|
|
} |
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region static
|
|
|
|
|
|
|
|
// 获取目录中的默认文件,返回文件的完整路径。
|
|
|
|
string GetDefaultFile(string directory) |
|
|
|
static string GetDefaultFile(string directory) |
|
|
|
{ |
|
|
|
var subs = StorageUtility.GetSubFiles(directory); |
|
|
|
if (subs.Count < 0) return null; |
|
|
@ -130,18 +232,16 @@ namespace Apewer.Web |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>列出子项。可指定筛选器,传入完整路径。</summary>
|
|
|
|
public void ListChildren(string directory) |
|
|
|
static Json ListChildren(string directory) |
|
|
|
{ |
|
|
|
if (!Directory.Exists(directory)) return; |
|
|
|
|
|
|
|
if (!Directory.Exists(directory)) return null; |
|
|
|
var json = Json.NewObject(); |
|
|
|
json.SetProperty("directories", ListDirectories(directory)); |
|
|
|
json.SetProperty("files", ListFiles(directory)); |
|
|
|
|
|
|
|
Response.Data = json; |
|
|
|
return json; |
|
|
|
} |
|
|
|
|
|
|
|
Json ListDirectories(string directory) |
|
|
|
static Json ListDirectories(string directory) |
|
|
|
{ |
|
|
|
var array = Json.NewArray(); |
|
|
|
var subs = StorageUtility.GetSubDirectories(directory); |
|
|
@ -171,7 +271,7 @@ namespace Apewer.Web |
|
|
|
return array; |
|
|
|
} |
|
|
|
|
|
|
|
Json ListFiles(string directory) |
|
|
|
static Json ListFiles(string directory) |
|
|
|
{ |
|
|
|
var array = Json.NewArray(); |
|
|
|
var subs = StorageUtility.GetSubFiles(directory); |
|
|
@ -199,20 +299,7 @@ namespace Apewer.Web |
|
|
|
return array; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>响应 403 状态。</summary>
|
|
|
|
public virtual void Respond403(string path) |
|
|
|
{ |
|
|
|
Response.Error("403"); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>响应 404 状态。</summary>
|
|
|
|
public virtual void Respond404(string path) |
|
|
|
{ |
|
|
|
Response.Error("404"); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>按扩展名获取 Content-Type。</summary>
|
|
|
|
public virtual string GetContentType(string extension) |
|
|
|
static string Mime(string extension) |
|
|
|
{ |
|
|
|
if (!string.IsNullOrEmpty(extension)) |
|
|
|
{ |
|
|
@ -335,70 +422,7 @@ namespace Apewer.Web |
|
|
|
return "application/octet-stream"; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>按扩展名获取过期时间,单位为秒,默认值为 0(不缓存)。</summary>
|
|
|
|
public virtual int GetExpires(string extension) => 0; |
|
|
|
|
|
|
|
// Server Side Includes
|
|
|
|
string ReadWithSSI(string path, int recursive = 0) |
|
|
|
{ |
|
|
|
if (recursive > 10) return ""; |
|
|
|
|
|
|
|
var input = StorageUtility.ReadFile(path, true); |
|
|
|
if (input == null || input.LongLength < 1) return ""; |
|
|
|
|
|
|
|
// 尝试以 UTF-8 解码。
|
|
|
|
var html = TextUtility.FromBinary(input); |
|
|
|
if (string.IsNullOrEmpty(html)) return ""; |
|
|
|
|
|
|
|
// 按首尾截取。
|
|
|
|
const string left = "<!--"; |
|
|
|
const string right = "-->"; |
|
|
|
const string head = "#include virtual="; |
|
|
|
var sb = new StringBuilder(); |
|
|
|
var text = html; |
|
|
|
while (true) |
|
|
|
{ |
|
|
|
var offset = text.IndexOf(left); |
|
|
|
if (offset < 0) |
|
|
|
{ |
|
|
|
sb.Append(text); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (offset > 0) |
|
|
|
{ |
|
|
|
sb.Append(text.Substring(0, offset)); |
|
|
|
text = text.Substring(offset + left.Length); |
|
|
|
} |
|
|
|
else text = text.Substring(left.Length); |
|
|
|
var length = text.IndexOf(right); |
|
|
|
if (length < 1) |
|
|
|
{ |
|
|
|
sb.Append(left); |
|
|
|
sb.Append(text); |
|
|
|
break; |
|
|
|
} |
|
|
|
var inner = text.Substring(0, length); |
|
|
|
var temp = inner.SafeTrim(); |
|
|
|
if (temp.StartsWith(head)) |
|
|
|
{ |
|
|
|
temp = temp.Substring(head.Length); |
|
|
|
temp = temp.Replace("\"", ""); |
|
|
|
var subPath = MapPath(temp); |
|
|
|
var subText = ReadWithSSI(subPath, recursive + 1); |
|
|
|
if (subText != null && subText.Length > 0) sb.Append(subText); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
sb.Append(left); |
|
|
|
sb.Append(inner); |
|
|
|
sb.Append(right); |
|
|
|
} |
|
|
|
text = text.Substring(length + right.Length); |
|
|
|
} |
|
|
|
|
|
|
|
var output = sb.ToString(); |
|
|
|
return output; |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|