前言
有一个项目,是用Asp.net MVC开发的,后来客户希望在系统项目不作过多改动的情况下,能够部署到Linux环境中,最终选择了使用Jexus(国产的,专为Asp.net 跨平台设计的)作为Asp.net程序的跨平台Web服务器部署环境(后来得知,可以使用 Nancy.Net 框架来实现跨平台),成功部署到Linux后,系统运行正常,后来,客户又提出新要求,对于html(有一些第三方投递过来的数据文件,放在其它目录,没有保存在View下的视图目录中)这种静态文件不能直接通过URL来访问,需要经过系统验证才可以正常访问,结果就开始了后面费劲费时的苦逼过程。
一、觉得很简单,直接使用最常用的方法
1) Web.config文件中增加以下配置:
<configuration>
<system.webServer>
<!--所有静态文件都要走路由-->
<modules runAllManagedModulesForAllRequests="true" />
<system.webServer>
</configuration>
2) 修改路由规则,在App_Start文件夹下RouteConfig.cs文件中的RegisterRoutes方法
public static void RegisterRoutes(RouteCollection routes)
{
//所有静态文件都要走路由,加上此代码
routes.RouteExistingFiles = true;
//系统默认生成的
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Content、Scripts中有js、css等文件,不能走路由
routes.IgnoreRoute("Content/{*relpath}");
routes.IgnoreRoute("Scripts/{*relpath}");
//html文件路由匹配,加上此代码
routes.MapRoute(name: "htmlData",
url: "File/Data/{*fileName}",
defaults: new { controller = "Login", action = "Index" });
//系统默认生成的
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional }
);
}
3) 生成测试,结果,windows和Linux完全无效,跑到网上百度一下,都说这是Asp.net mvc3的方法,对Asp.net mvc3有效,我的项目是Asp.net mvc4的,折腾了好些时间,就是不行;分析发现,html等常用的静态文件类型完全没有进入asp.net mvc的生命周期,请求到达Web服务器后,如果是静态文件,IIS或jexus直接去磁盘上查找,找到直接返回,没找到,再经过 Asp.net mvc来找 ,所以问题点是:怎样让html等静态 文件进入asp.net mvc的生命周期?
二、URL重定向,Jexus中设置访问.html文件时,重定向到一个中间的控制器的Action
原理:重定向到控制器后,就进入了Asp.net MVC的生命周期,这时就可以很容易判断是否授权登录访问了
1) 在Controller中增加Action
[Authorize] //验证是否登录,不然会跳转到,需在Web.config中配置
public ActionResult CheckGetData()
{
.....省略
StreamReader sr=new StreamReader(fileName);
string str=sr.ReadToEnd();
sr.Close();
Reponse.ContentType="text/html";
Response.Write(str);
.....省略
}
Web.config配置:
<configuration>
<system.web>
<!--非登录时将直接跳转至登录页面-->
<authentication mode="Forms">
<forms loginUrl="~/Login/Index/"></forms>
</authentication>
</system.web>
</configuration>
2) 在jexus/siteconf文件夹找到站点配置文件,配置UrlRewrite:
rewrite=*.html /Test/CheckGetData/
3) 生成并测试,结果:html文件可正常读取,如果只是独立的html文件没有引用其它任何文件(js、css等),可以正常显示,否则,html中引用的自己相对路径js、css、jpg等全部都变成了http://localhost/Test/CheckGetData/scripts/xxx.js、
http://localhost/Test/CheckGetData/content/xxx.jpg、
http://localhost/Test/CheckGetData/content/xxx.css等形式,
分析发现,html、content、scripts在项目中是在另外的文件夹中,而非View下,此种方法是通过路由的路径去加载文件的,肯定不行!
三、直接更改html的后缀为
1) 将html的后缀改为aspx;
2) 结合上述“一"中的方法;
3) 生成并测试,结果:不行,要么直接跳转到登录页,要么显示的是乱码;
四、使用实现的IHttpHandler、IRouteHandler接口
1) 将html的后缀改为aspx;
2) 在项目中新建StaticHttpHandler项目或cs文件,实现代码如下:
//继承IHttpHandler
public class StaticHttpHandler:IHttpHandler
{
//注意是RequestContext类型
public StaticHttpHandler(RequestContext requestContext)
{
ProcessRequest(requestContext);
}
//注意是RequestContext类型
private void ProcessRequest(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
//请求过滤判断
if (request.Url.ToString().ToLower().Contains(".aspx"))
{
//未登录授权
if (requestContext.HttpContext.User == null || !requestContext.HttpContext.User.Identity.IsAuthenticated)
{
requestContext.HttpContext.Response.Redirect("/Login/Index/");
}
else
{
requestContext.HttpContext.Response.ContentType = "text/html";
requestContext.HttpContext.Response.WriteFile(request.Url.AbsolutePath);
requestContext.HttpContext.Response.End();
}
}
}
//实现IHttpHandler中的方法
public void ProcessRequest(HttpContext context)
{
}
//实现IHttpHandler中的方法
public bool IsReusable
{
get{return true;}
}
}
3) 在项目中新建StaticRouteHandler项目或cs文件,实现代码如下:
//继承IRouteHandler
public class StaticRouteHandler : IRouteHandler
{
//实现IRouteHandler中的GetHttpHandler方法,返回一个HttpHandler
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new StaticHttpHandler(requestContext);
}
}
4) 2) 修改路由规则,在App_Start文件夹下RouteConfig.cs文件中的RegisterRoutes方法
public static void RegisterRoutes(RouteCollection routes)
{
//所有静态文件都要走路由,加上此代码
routes.RouteExistingFiles = true;
//系统默认生成的
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Content、Scripts中有js、css等文件,不能走路由
routes.IgnoreRoute("Content/{*relpath}");
routes.IgnoreRoute("Scripts/{*relpath}");
//html文件路由匹配,加上此代码
routes.Add("StaticHttpHandler",
new Route("Files/Data/{*fileName}", new StaticRouteHandler()));
//系统默认生成的
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional }
);
}
5) 生成并测试,结果:达到目的!
总结:
1) 常用的html、jpg等静态文件Web服务器(IIS、jexus等)会直接到磁盘上匹配查找返回,不经过Asp.net的生命周期,所以就无法对其进行验证;
2) 对某些静态文件的后缀改为aspx(文件结构实质没质)有时候能解决一些问题;
3) 可以对于些特殊文件的请求使用UrlRewrite(一般可以是IIS等自带的重定向功能,也可以使用微软的UrlRewrite.Net插件,使用Nuget在项目中引用)功能隐藏原始的URL,有一定的安全保护作用;
4) 需要学习理解Asp.net 的Http请求流程、IHttpHandler、IRouteHandler、IHttpHandlerFactory 和IHttpModule的相关原理知识,这些知识道对Asp.net WebForm和Asp.net MVC都适用;
5) 可以使用Nancy.Net框架来开发.net的跨平台Web应用;
最后,本文章中的解决办法及出现的结果只是本人遇到的,并不代表一定是这种形式和结果,目的也是让供大家参考,希望有帮助,同时自己也加强记忆及以后翻阅!