我的问题与此类似:
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/。
所以我的问题是,怎样处理这种情况才是正确的?
虽然Chris Baxter的回答有助于解决原始问题,但当应用程序托管在虚拟目录中时,它在我的情况下不起作用。在研究了这些选项之后,我完成了DIY解决方案。
ProperStyleBundle类包括从原始CssRewriteUrlTransform中借来的代码,用于正确转换虚拟目录中的相对路径。如果文件不存在,它也会抛出,并阻止bundle中的文件重新排序(代码取自BetterStyleBundle)。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Optimization;
using System.Linq;
namespace MyNamespace
{
public class ProperStyleBundle : StyleBundle
{
public override IBundleOrderer Orderer
{
get { return new NonOrderingBundleOrderer(); }
set { throw new Exception( "Unable to override Non-Ordered bundler" ); }
}
public ProperStyleBundle( string virtualPath ) : base( virtualPath ) {}
public ProperStyleBundle( string virtualPath, string cdnPath ) : base( virtualPath, cdnPath ) {}
public override Bundle Include( params string[] virtualPaths )
{
foreach ( var virtualPath in virtualPaths ) {
this.Include( virtualPath );
}
return this;
}
public override Bundle Include( string virtualPath, params IItemTransform[] transforms )
{
var realPath = System.Web.Hosting.HostingEnvironment.MapPath( virtualPath );
if( !File.Exists( realPath ) )
{
throw new FileNotFoundException( "Virtual path not found: " + virtualPath );
}
var trans = new List<IItemTransform>( transforms ).Union( new[] { new ProperCssRewriteUrlTransform( virtualPath ) } ).ToArray();
return base.Include( virtualPath, trans );
}
// This provides files in the same order as they have been added.
private class NonOrderingBundleOrderer : IBundleOrderer
{
public IEnumerable<BundleFile> OrderFiles( BundleContext context, IEnumerable<BundleFile> files )
{
return files;
}
}
private class ProperCssRewriteUrlTransform : IItemTransform
{
private readonly string _basePath;
public ProperCssRewriteUrlTransform( string basePath )
{
_basePath = basePath.EndsWith( "/" ) ? basePath : VirtualPathUtility.GetDirectory( basePath );
}
public string Process( string includedVirtualPath, string input )
{
if ( includedVirtualPath == null ) {
throw new ArgumentNullException( "includedVirtualPath" );
}
return ConvertUrlsToAbsolute( _basePath, input );
}
private static string RebaseUrlToAbsolute( string baseUrl, string url )
{
if ( string.IsNullOrWhiteSpace( url )
|| string.IsNullOrWhiteSpace( baseUrl )
|| url.StartsWith( "/", StringComparison.OrdinalIgnoreCase )
|| url.StartsWith( "data:", StringComparison.OrdinalIgnoreCase )
) {
return url;
}
if ( !baseUrl.EndsWith( "/", StringComparison.OrdinalIgnoreCase ) ) {
baseUrl = baseUrl + "/";
}
return VirtualPathUtility.ToAbsolute( baseUrl + url );
}
private static string ConvertUrlsToAbsolute( string baseUrl, string content )
{
if ( string.IsNullOrWhiteSpace( content ) ) {
return content;
}
return new Regex( "url\\(['\"]?(?<url>[^)]+?)['\"]?\\)" )
.Replace( content, ( match =>
"url(" + RebaseUrlToAbsolute( baseUrl, match.Groups["url"].Value ) + ")" ) );
}
}
}
}
像StyleBundle一样使用它:
bundles.Add( new ProperStyleBundle( "~/styles/ui" )
.Include( "~/Content/Themes/cm_default/style.css" )
.Include( "~/Content/themes/custom-theme/jquery-ui-1.8.23.custom.css" )
.Include( "~/Content/DataTables-1.9.4/media/css/jquery.dataTables.css" )
.Include( "~/Content/DataTables-1.9.4/extras/TableTools/media/css/TableTools.css" ) );
更好的是(IMHO)实现一个自定义Bundle来修复图像路径。我为我的应用程序写了一个。
using System;
using System.Collections.Generic;
using IO = System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Optimization;
...
public class StyleImagePathBundle : Bundle
{
public StyleImagePathBundle(string virtualPath)
: base(virtualPath, new IBundleTransform[1]
{
(IBundleTransform) new CssMinify()
})
{
}
public StyleImagePathBundle(string virtualPath, string cdnPath)
: base(virtualPath, cdnPath, new IBundleTransform[1]
{
(IBundleTransform) new CssMinify()
})
{
}
public new Bundle Include(params string[] virtualPaths)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
// Debugging. Bundling will not occur so act normal and no one gets hurt.
base.Include(virtualPaths.ToArray());
return this;
}
// In production mode so CSS will be bundled. Correct image paths.
var bundlePaths = new List<string>();
var svr = HttpContext.Current.Server;
foreach (var path in virtualPaths)
{
var pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase);
var contents = IO.File.ReadAllText(svr.MapPath(path));
if(!pattern.IsMatch(contents))
{
bundlePaths.Add(path);
continue;
}
var bundlePath = (IO.Path.GetDirectoryName(path) ?? string.Empty).Replace(@"\", "/") + "/";
var bundleUrlPath = VirtualPathUtility.ToAbsolute(bundlePath);
var bundleFilePath = String.Format("{0}{1}.bundle{2}",
bundlePath,
IO.Path.GetFileNameWithoutExtension(path),
IO.Path.GetExtension(path));
contents = pattern.Replace(contents, "url($1" + bundleUrlPath + "$2$1)");
IO.File.WriteAllText(svr.MapPath(bundleFilePath), contents);
bundlePaths.Add(bundleFilePath);
}
base.Include(bundlePaths.ToArray());
return this;
}
}
要使用它,请执行:
bundles.Add(new StyleImagePathBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
...而不是……
bundles.Add(new StyleBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
What it does is (when not in debug mode) looks for url(<something>) and replaces it with url(<absolute\path\to\something>). I wrote the thing about 10 seconds ago so it might need a little tweaking. I've taken into account fully-qualified URLs and base64 DataURIs by making sure there's no colons (:) in the URL path. In our environment, images normally reside in the same folder as their css files, but I've tested it with both parent folders (url(../someFile.png)) and child folders (url(someFolder/someFile.png).
只需要记住在一个包中修复多个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()));