巧用asp.net 过滤所有的Response请求并替换部分内容,彻底解决MVC虚拟路径问题.
如题:
我的项目需要用到mvc,但是用习惯了asp.net的aspx文件形式,用mvc还真有点不习惯,
首当其冲的就是路径问题...
众所周知,
mvc最大的特点就是重用,重用页面.重用控制器..
但是比较郁闷的是,页面中的文件引用,图片路径问题真是无法解决.
用/代表的是网站的根目录.. 注意是浏览器的urlwww.***.com/中的这第一个/
用./和../代表的是相对路径.但是这路径也是相对浏览器的url来指定的, 比如 www.***.com/Home/Index指向Index.aspx, 其中写了一句话
<img src="./images/a.gif" alt="" />
如果Index.aspx被另外一个 www.***.com/Home/User/Index 路径引用
则
这个图片将失效.因为只要是在客户端的路径都是相对浏览器url的.
所以我们的Index.aspx将不可重用..
但是折中的方法就是把Index.aspx中的这句话改成
<img src="/images/a.gif" alt="" />
这样做重用问题算是解决了,可是还有一个致命的虚拟目录问题..
如果把你的网站移植到一个子目录下.则整个程序将全部是错误..
所以最好的方法就是把路径全部相对于你的项目..从你的项目根目录开始算起..这样可移植性就高很多.
但是,浏览器根本不知道你的项目根目录是那一层..
我们知道asp.net提供了从根目录开始的方法, ~/
~/指向的就是当前项目的根目录..
不过比较悲剧这个~/只只能在服务器端用.. 客户端根本不认识~/
所以就有了下文..
我在服务器端,把所有的输出都过滤一遍,如果输出的文本中包含 ~/ 就把 ~/ 替换成 项目在网站中的从根目录 / 开始的绝对路径
所以以后的写法就是
<img src="~/images/a.gif" alt="" />
以后你的项目就随便移植和重用吧..哈哈..
当然别忘啦所有的路径都从 ~/ 开始哦
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
namespace HttpModules
{
public class ResponseFilter : IHttpModule
{
private string vriualRootPath;
void IHttpModule.Dispose() { }
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += (sender, e) =>
{
var f = new RootPathReplaceStream(context.Response.Filter);
context.Response.Filter = f;
};
}
}
public class RootPathReplaceStream : Stream
{
private Stream output;
public RootPathReplaceStream(Stream s)
{
output = s;
}
public override bool CanRead
{
get { return output.CanRead; }
}
public override bool CanSeek
{
get { return output.CanSeek; }
}
public override bool CanWrite
{
get { return output.CanWrite; }
}
public override void Flush()
{
output.Flush();
}
public override long Length
{
get { return output.Length; }
}
public override long Position
{
get { return output.Position; }
set { output.Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return output.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return output.Seek(offset, origin);
}
public override void SetLength(long value)
{
output.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
StringComparison ignore = StringComparison.CurrentCultureIgnoreCase;
if (HttpContext.Current != null)
{
HttpContext context = HttpContext.Current;
if (context.Response.ContentType.Equals("text/html", ignore))
{
Encoding encoding = context.Response.ContentEncoding;
//在這邊把 google 換成 microsoft
string html = encoding.GetString(buffer, offset, count);
// html.Replace("google", "microsoft");
//做自己的处理
//註冊事件,在 BeginRequest 的時候把 Response.Filter 換掉
//此种替换方法不太严谨
//html = html.Replace("~/", VriualRootPath);
//替换 "~/ 为虚拟路径 这样只替换带"号的目录
html = html.Replace("\"~/", "\"" + VriualRootPath);
//替换 '~/ 为虚拟路径 这样只替换带'号的目录
html = html.Replace("'~/", "'" + VriualRootPath);
byte[] bytes = encoding.GetBytes(html);
output.Write(bytes, 0, bytes.Length);
}
else
{
output.Write(buffer, offset, count);
}
}
}
public static string _VriualRootPath;
public string VriualRootPath
{
get
{
if (_VriualRootPath == null)
{
//计算当前虚拟目录的相对路径
string absRootPath = HttpContext.Current.Server.MapPath("/");
_VriualRootPath = HttpContext.Current.Server.MapPath("~/");
_VriualRootPath = VriualRootPath.Replace(absRootPath, "/");
_VriualRootPath = VriualRootPath.Replace("\\", "/");// \ 变成 /
}
return _VriualRootPath;
}
set {
_VriualRootPath = value;
}
}
}
}