我的问题与此类似:
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/。
所以我的问题是,怎样处理这种情况才是正确的?
根据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()));
我有这个问题与捆绑有不正确的路径的图像和CssRewriteUrlTransform不解决相对父路径..正确(也有外部资源的问题,如webfonts)。这就是为什么我写了这个自定义转换(似乎正确地完成了上面所有的工作):
public class CssRewriteUrlTransform2 : IItemTransform
{
public string Process(string includedVirtualPath, string input)
{
var pathParts = includedVirtualPath.Replace("~/", "/").Split('/');
pathParts = pathParts.Take(pathParts.Count() - 1).ToArray();
return Regex.Replace
(
input,
@"(url\(['""]?)((?:\/??\.\.)*)(.*?)(['""]?\))",
m =>
{
// Somehow assigning this to a variable is faster than directly returning the output
var output =
(
// Check if it's an aboslute url or base64
m.Groups[3].Value.IndexOf(':') == -1 ?
(
m.Groups[1].Value +
(
(
(
m.Groups[2].Value.Length > 0 ||
!m.Groups[3].Value.StartsWith('/')
)
) ?
string.Join("/", pathParts.Take(pathParts.Count() - m.Groups[2].Value.Count(".."))) :
""
) +
(!m.Groups[3].Value.StartsWith('/') ? "/" + m.Groups[3].Value : m.Groups[3].Value) +
m.Groups[4].Value
) :
m.Groups[0].Value
);
return output;
}
);
}
}
编辑:我没有意识到,但我在代码中使用了一些自定义扩展方法。它们的源代码是:
/// <summary>
/// Based on: http://stackoverflow.com/a/11773674
/// </summary>
public static int Count(this string source, string substring)
{
int count = 0, n = 0;
while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
{
n += substring.Length;
++count;
}
return count;
}
public static bool StartsWith(this string source, char value)
{
if (source.Length == 0)
{
return false;
}
return source[0] == value;
}
当然,应该可以将string . startswith (char)替换为string . startswith (string)。
这是一个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);
}
}