我的问题与此类似:
ASP。NET MVC 4缩小和背景图像
除了我想坚持MVC自己的捆绑如果我可以的话。我有一个大脑崩溃试图找出什么是正确的模式是指定样式包,如独立的css和图像集,如jQuery UI工作。
我有一个典型的MVC网站结构与/Content/css包含我的基本css,如样式。css。在css文件夹中,我还有子文件夹,如/jquery-ui,其中包含css文件和/images文件夹。jQuery UI CSS中的图像路径是相对于该文件夹的,我不想打乱它们。
根据我的理解,当我指定StyleBundle时,我需要指定一个虚拟路径,它也不匹配真实的内容路径,因为(假设我忽略了到内容的路由)IIS将尝试将该路径解析为物理文件。所以我指定:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/jquery-ui/*.css"));
呈现的使用:
@Styles.Render("~/Content/styles/jquery-ui")
我可以看到请求发送到:
http://localhost/MySite/Content/styles/jquery-ui?v=nL_6HPFtzoqrts9nwrtjq0VQFYnhMjY5EopXsK8cxmg1
这将返回正确的、最小化的CSS响应。
但随后浏览器会发送一个相对链接图像的请求,如下:
http://localhost/MySite/Content/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
这是404。
我知道我的URL jquery-ui的最后一部分是一个无扩展的URL,我的包的处理程序,所以我可以看到为什么图像的相对请求是简单的/styles/images/。
所以我的问题是,怎样处理这种情况才是正确的?
只需要记住在一个包中修复多个CSS包含,例如:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/path1/somestyle1.css", "~/Content/css/path2/somestyle2.css"));
你不能只添加新的CssRewriteUrlTransform()到末尾,因为你可以用一个CSS文件的方法不支持它,所以你必须多次使用Include:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/path1/somestyle1.css", new CssRewriteUrlTransform())
.Include("~/Content/css/path2/somestyle2.css", new CssRewriteUrlTransform()));
这是一个Bundle Transform,它将用相对于css文件的url替换css url。只要把它添加到你的bundle中,它就会解决这个问题。
public class CssUrlTransform: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response) {
Regex exp = new Regex(@"url\([^\)]+\)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
foreach (FileInfo css in response.Files) {
string cssAppRelativePath = css.FullName.Replace(context.HttpContext.Request.PhysicalApplicationPath, context.HttpContext.Request.ApplicationPath).Replace(Path.DirectorySeparatorChar, '/');
string cssDir = cssAppRelativePath.Substring(0, cssAppRelativePath.LastIndexOf('/'));
response.Content = exp.Replace(response.Content, m => TransformUrl(m, cssDir));
}
}
private string TransformUrl(Match match, string cssDir) {
string url = match.Value.Substring(4, match.Length - 5).Trim('\'', '"');
if (url.StartsWith("http://") || url.StartsWith("data:image")) return match.Value;
if (!url.StartsWith("/"))
url = string.Format("{0}/{1}", cssDir, url);
return string.Format("url({0})", url);
}
}
从v1.1.0-alpha1(预发布包)开始,框架使用VirtualPathProvider来访问文件,而不是接触物理文件系统。
更新后的变压器如下所示:
public class StyleRelativePathTransform
: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase);
response.Content = string.Empty;
// open each of the files
foreach (var file in response.Files)
{
using (var reader = new StreamReader(file.Open()))
{
var contents = reader.ReadToEnd();
// apply the RegEx to the file (to change relative paths)
var matches = pattern.Matches(contents);
if (matches.Count > 0)
{
var directoryPath = VirtualPathUtility.GetDirectory(file.VirtualPath);
foreach (Match match in matches)
{
// this is a path that is relative to the CSS file
var imageRelativePath = match.Groups[2].Value;
// get the image virtual path
var imageVirtualPath = VirtualPathUtility.Combine(directoryPath, imageRelativePath);
// convert the image virtual path to absolute
var quote = match.Groups[1].Value;
var replace = String.Format("url({0}{1}{0})", quote, VirtualPathUtility.ToAbsolute(imageVirtualPath));
contents = contents.Replace(match.Groups[0].Value, replace);
}
}
// copy the result into the response.
response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
}
}
}
}
没有必要指定转换或使用疯狂的子目录路径。经过大量的故障排除后,我将其隔离到这个“简单”规则中(这是一个bug吗?)……
如果您的包路径不是以包含的项目的相对根开始,那么web应用程序根将不会被考虑在内。
对我来说,听起来更像是一个bug,但无论如何,这就是你在当前的。net 4.51版本中修复它的方法。也许其他的答案对于旧的ASP是必要的。NET构建,不能说没有时间对所有这些进行回顾性测试。
为了澄清,这里有一个例子:
我有这些文件…
~/Content/Images/Backgrounds/Some_Background_Tile.gif
~/Content/Site.css - references the background image relatively, i.e. background: url('Images/...')
然后像这样设置bundle…
BundleTable.Add(new StyleBundle("~/Bundles/Styles").Include("~/Content/Site.css"));
然后渲染成…
@Styles.Render("~/Bundles/Styles")
并获得“行为”(bug), CSS文件本身有应用程序根(例如。“http://localhost:1234/MySite/Content/Site.css”),但CSS图像内都开始“/Content/Images/…”或“/Images/…”,这取决于我是否添加转换。
甚至尝试创建“Bundles”文件夹,看看它是否与路径存在或不存在有关,但这并没有改变任何东西。该问题的解决方案实际上是要求包的名称必须以路径根开头。
这意味着这个例子通过像..这样注册和呈现bundle路径来修复。
BundleTable.Add(new StyleBundle("~/Content/StylesBundle").Include("~/Content/Site.css"));
...
@Styles.Render("~/Content/StylesBundle")
所以你当然可以说这是RTFM,但我很确定我和其他人从默认模板或MSDN或ASP文档的某处获取了这个“~/Bundles/…”路径。NET网站,或者只是偶然发现的,因为它实际上是一个非常符合逻辑的虚拟路径名称,选择这样的虚拟路径是有意义的,因为它与实际目录不冲突。
不管怎样,事情就是这样。微软没有发现漏洞。我不同意这一点,要么它应该像预期的那样工作,要么抛出一些异常,要么额外重写添加bundle路径,选择包含应用程序根或不包含根。我无法想象为什么有人不希望包含一个应用程序根(通常情况下,除非你安装了一个DNS别名/默认的网站根)。这应该是默认值。
根据MVC4 css bundle和图片引用的这个线程,如果你定义你的bundle为:
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css"));
如果将bundle定义在与组成bundle的源文件相同的路径上,则相对映像路径仍然有效。bundle路径的最后一部分实际上是特定bundle的文件名(例如,/bundle可以是您喜欢的任何名称)。
这只在你将CSS从同一个文件夹中捆绑在一起时才有效(我认为从捆绑的角度来看这是有意义的)。
更新
根据下面@Hao Kung的评论,这也可以通过应用CssRewriteUrlTransformation(在捆绑时更改CSS文件的相对URL引用)来实现。
注意:我还没有确认关于重写到虚拟目录中的绝对路径的问题的评论,所以这可能不适用于每个人(?)
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css",
new CssRewriteUrlTransform()));