我正在制作一个网站,用户可以将自己的HTML代码上传到网站,然后在呼叫特定子域时,网站将显示他们的网站。
带有附件的HTML代码将其上传到网站内的子目录:
SITE #1
~/sites/test1/index.html
~/sites/test1/images/logo.png
SITE #2
~/sites/test2/index.html
~/sites/test2/images/logo.png
因此,您可以使用以下URL调用这些文件:
SITE #1
http://test1.mydomain.com/index.html
http://test1.mydomain.com/images/logo.png
SITE #2
http://test2.mydomain.com/index.html
http://test2.mydomain.com/images/logo.png
所以我所做的是在global.asax中进行错误处理程序,该操作器检测到您何时尝试请求不存在的文件,因此请求网站:
protected void Application_Error()
{
// Get the subdomain requested
var subdomain = Request.Url.Authority.Split(new char[] { '.', ':' }).FirstOrDefault();
// Get the directory info about the requested subdomain
DirectoryInfo info = new DirectoryInfo(Server.MapPath("~/" + subdomain));
// Check if subdomain is not empty and exists
if (!string.IsNullOrEmpty(subdomain) && info.Exists)
{
// Get the requested filename
var filename = Request.Url.PathAndQuery.Split(new char[] { '?' }).FirstOrDefault();
// If the root is requested change to index.html
if (filename == "/") filename = "/index.html";
// Translate requested filename to server path
var fullname = Server.MapPath("~/sites/" + subdomain + filename);
// Respond the file
ResponseFile(fullname);
}
else
{
// Subdomain not found so end the request
Response.End();
}
}
public void ResponseFile(string fullname)
{
Response.Clear();
System.IO.Stream oStream = null;
try
{
// Open the file
oStream =
new System.IO.FileStream
(path: fullname,
mode: System.IO.FileMode.Open,
share: System.IO.FileShare.Read,
access: System.IO.FileAccess.Read);
// **************************************************
Response.Buffer = false;
// Setting the ContentType
Response.ContentType = MimeMapping.GetMimeMapping(fullname);
// Get the length of the file
long lngFileLength = oStream.Length;
// Notify user (client) the total file length
Response.AddHeader("Content-Length", lngFileLength.ToString());
// **************************************************
// Total bytes that should be read
long lngDataToRead = lngFileLength;
// Read the bytes of file
while (lngDataToRead > 0)
{
// The below code is just for testing! So we commented it!
//System.Threading.Thread.Sleep(200);
// Verify that the client is connected or not?
if (Response.IsClientConnected)
{
// 8KB
int intBufferSize = 8 * 1024;
// Create buffer for reading [intBufferSize] bytes from file
byte[] bytBuffers =
new System.Byte[intBufferSize];
// Read the data and put it in the buffer.
int intTheBytesThatReallyHasBeenReadFromTheStream =
oStream.Read(buffer: bytBuffers, offset: 0, count: intBufferSize);
// Write the data from buffer to the current output stream.
Response.OutputStream.Write
(buffer: bytBuffers, offset: 0,
count: intTheBytesThatReallyHasBeenReadFromTheStream);
// Flush (Send) the data to output
// (Don't buffer in server's RAM!)
Response.Flush();
lngDataToRead =
lngDataToRead - intTheBytesThatReallyHasBeenReadFromTheStream;
}
else
{
// Prevent infinite loop if user disconnected!
lngDataToRead = -1;
}
}
}
catch { }
finally
{
if (oStream != null)
{
//Close the file.
oStream.Close();
oStream.Dispose();
oStream = null;
}
Response.Close();
Response.End();
}
}
上面的代码适用于"/index.html"文件,但它对"/images/logo.png"不起作用,因为404不会启动application_error处理程序。经过大量的搜索和拉出头发,我发现此"功能"从.NET 4.0及以上开始。但是我不想回去,我想知道如何正确解决这个问题。
等到管道中的应用程序错误有点晚。一种方法是创建自定义处理程序,并使用自定义路由来检测虚拟文件将这些请求映射到处理程序。这意味着您需要使用可预测的模式生成指向虚拟文件的链接,也许是在/specialfiles/:
之类的路径上。routes.Add(new Route("SpecialFiles/{*path}", new SomeFileHandler()));
您还可以将其映射到控制器操作中,然后让操作解析URL/QUERY字符串并返回文件响应。
任何一种方法都允许您指定具有各种参数的路由,例如访问类似于其他系统中"共享文件"链接的文件所需的高度随机令牌。您可以配置在特定文件扩展名上匹配的路由。选项非常多样化。就像其他任何路线一样,您可以将路径的不同部分推入变量,也可以在进入处理程序或操作后直接从请求访问URL并手动解析。
多亏了aaronls,我开始搜索如何制作一个可以捕获所有请求的自定义处理程序。太糟糕了,这并不容易找到。
首先,您需要通知IIS您要通过更新Web.config来处理所有文件:
<system.webServer>
<httpErrors existingResponse="PassThrough" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication"/>
</modules>
</system.webServer>
(我不知道它 httperrors asutherResponse =" PassThrough" 确实需要,可能是我尝试过的一些解决方案(
然后,我需要制作自己的自定义处理程序并将其设置在RouteConfig中:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// So my users can still login
routes.MapRoute(
name: "Account",
url: "Account/{action}/{id}",
defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }
);
// For the upload controller to work
routes.MapRoute(
name: "Upload",
url: "Upload/{action}/{id}",
defaults: new { controller = "Upload", action = "Index", id = UrlParameter.Optional }
);
// And finally registrating my custom handler
routes.Add(new Route("{*path}", new CustomRouteHandler()));
// This was the original routeconfig
//routes.MapRoute(
// name: "Default",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//);
}
}
public class CustomRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomHttpHandler();
}
}
public class CustomHttpHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return false;
}
}
public void ProcessRequest(HttpContext context)
{
// Get the subdomain requested
var subdomain = context.Request.Url.Authority.Split(new char[] { '.', ':' }).FirstOrDefault();
// Get the directory info about the requested subdomain
DirectoryInfo info = new DirectoryInfo(context.Server.MapPath("~/Websites/" + subdomain));
// Check if subdomain is not empty and exists
if (!string.IsNullOrEmpty(subdomain) && info.Exists)
{
// Get the requested filename
var filename = context.Request.Url.PathAndQuery.Split(new char[] { '?' }).FirstOrDefault();
// If the root is requested change to index.html
if (filename == "/") filename = "/index.html";
// Translate requested filename to server path
var fullname = context.Server.MapPath("~/Websites/" + subdomain + filename);
// Respond the file
ResponseFile(context, fullname);
}
else
{
// Subdomain not found so end the request
context.Response.End();
}
}
public void ResponseFile(HttpContext context, string fullname)
{
// Clear the response buffer
context.Response.Clear();
System.IO.Stream oStream = null;
try
{
// Open the file
oStream =
new System.IO.FileStream
(path: fullname,
mode: System.IO.FileMode.Open,
share: System.IO.FileShare.Read,
access: System.IO.FileAccess.Read);
// **************************************************
context.Response.Buffer = false;
// Setting the ContentType
context.Response.ContentType = MimeMapping.GetMimeMapping(fullname);
// Get the length of the file
long lngFileLength = oStream.Length;
// Notify user (client) the total file length
context.Response.AddHeader("Content-Length", lngFileLength.ToString());
// **************************************************
// Total bytes that should be read
long lngDataToRead = lngFileLength;
// Read the bytes of file
while (lngDataToRead > 0)
{
// Verify that the client is connected or not?
if (context.Response.IsClientConnected)
{
// 8KB
int intBufferSize = 8 * 1024;
// Create buffer for reading [intBufferSize] bytes from file
byte[] bytBuffers =
new System.Byte[intBufferSize];
// Read the data and put it in the buffer.
int intTheBytesThatReallyHasBeenReadFromTheStream =
oStream.Read(buffer: bytBuffers, offset: 0, count: intBufferSize);
// Write the data from buffer to the current output stream.
context.Response.OutputStream.Write
(buffer: bytBuffers, offset: 0,
count: intTheBytesThatReallyHasBeenReadFromTheStream);
// Flush (Send) the data to output
// (Don't buffer in server's RAM!)
context.Response.Flush();
lngDataToRead =
lngDataToRead - intTheBytesThatReallyHasBeenReadFromTheStream;
}
else
{
// Prevent infinite loop if user disconnected!
lngDataToRead = -1;
}
}
}
catch (Exception e)
{
}
finally
{
if (oStream != null)
{
//Close the file.
oStream.Close();
oStream.Dispose();
oStream = null;
}
context.Response.Close();
context.Response.End();
}
}
}