Stopping MVC ViewMasterPage from resolving CSS URLs - asp.net-mvc

By default Master pages in .NET MVC2 placed like this /folderlevel1/folderlevel2/Site.master accessed from the url domain.com/urllevel1/urllevel2/ will resolve the URL in this tag:
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
to
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
This becomes problematic in my multi-tennant MVC app. And I want to stop this behaviour. I want the master page to leave the url alone.

You are probably having this issue because ASP.NET performs magic tricks when you specify the head tag as a server side control like so:
<head runat="server">
These tricks include:
resolving relative CSS paths
populating title and meta tags from your view's #Page directive
If you don't want these tricks, simply remove the runat attribute from the head tag:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<!DOCTYPE html>
<html>
<head>
<link href="Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
...
</body>
</html>

you can use
<link href="<%=Url.Content("~/Content/Site.css")%>" rel="stylesheet" type="text/css" />
but that basically always translates to this:
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
so you might as well just use the latter.

Like mentioned on Kazi's best practices entry (http://weblogs.asp.net/rashid/archive/2009/04/03/asp-net-mvc-best-practices-part-2.aspx), ignore routing when accessing resources. To do this it's very simple and works well. Add the below to your AddRoutes function in Global.asax
_routes.IgnoreRoute("assets/{*pathInfo}");
...where "assets/" is your content folder (by default it's "Content")

oscar,
i'm sure there will be many similar answers to follow, but the standard way would be:
<link href="<%=Url.Content("~/Content/Site.css")%>" rel="stylesheet" type="text/css" />
I may have missed something subtle here of course :)

I suggest using an extension method for the HtmlHelper to take care of this task for you.
using System.Web;
using System.Web.Mvc;
namespace MyApplicationNamepsace.Views
{
public static class HtmlExtensions
{
public static IHtmlString RelativeCssLink(this HtmlHelper helper, string fileNameAndRelativePath)
{
TagBuilder builder = new TagBuilder("link");
builder.Attributes.Add("rel", "stylesheet");
builder.Attributes.Add("type", "text/css");
builder.Attributes.Add("href", fileNameAndRelativePath);
IHtmlString output = new HtmlString(builder.ToString());
return output;
}
}
}
Then make sure you add the namespace to the web.config file in the views folder.
<system.web>
<pages>
<namespaces>
<add namespace="MyApplicationNamespace.Views"/>
</namespaces>
</pages>
</system.web>
Then use it in your masterpage.
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<%: Html.RelativeCssLink("Content/Site.css") %>
</head>

Related

Asp.net MVC application using typescript and Phaser.io does not show images

I have a simple typescript game(using Phaser.io) that i what to run from an ASP.net MVC application in an MVC 5 View page with Layout (Razor)
I have added the view and the controller
/Views/Home/About.cshtml
#{
ViewBag.Title = "About";
}
<link rel="stylesheet" href="app.css" type="text/css" />
<script src="~/phaser.js"></script>
<script src="~/app.js"></script>
<div id="game"></div>
The game starts but it does not show any images. It looks like the reference to the image is wrong.
http://asskicker3.azurewebsites.net/Home/About
I reference the images as follows in the app.ts:
preload() {
this.game.load.image('background',"assets/background.jpg");
If i add an HTML page to the root of my folder it all work perfect.
http://asskicker3.azurewebsites.net/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body style="margin:0px; padding: 0px;">
<link rel="stylesheet" href="app.css" type="text/css" />
<script src="phaser.js"></script>
<script src="app.js"></script>
<div id="game"></div>
</body>
</html>
Answer:
James Skemp solutions works. Just by adding the / it all works. Perfect!
Your asset references are relative.
So if you look at the network tab in a browser you'll notice that it's trying to load the graphics relative to the URL. For example, http://asskicker3.azurewebsites.net/Home/assets/background.jpg
One way to fix this would be to change your preload so that the asset URLs are absolute instead of relative. So
this.game.load.image('background', "assets/background.jpg");
would become
this.game.load.image('background', "/assets/background.jpg");

Internel URL for .ear file deployment [duplicate]

To working my static file (CSS, JS) I have to write absolute path like /AppName/templates/style/main.css. Is there any solution, that I could write relative path like style/main.css?
If your actual concern is the dynamicness of the webapp context (the "AppName" part), then just retrieve it dynamically by HttpServletRequest#getContextPath().
<head>
<link rel="stylesheet" href="${pageContext.request.contextPath}/templates/style/main.css" />
<script src="${pageContext.request.contextPath}/templates/js/main.js"></script>
<script>var base = "${pageContext.request.contextPath}";</script>
</head>
<body>
link
</body>
If you want to set a base path for all relative links so that you don't need to repeat ${pageContext.request.contextPath} in every relative link, use the <base> tag. Here's an example with help of JSTL functions.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
...
<head>
<c:set var="url">${pageContext.request.requestURL}</c:set>
<base href="${fn:substring(url, 0, fn:length(url) - fn:length(pageContext.request.requestURI))}${pageContext.request.contextPath}/" />
<link rel="stylesheet" href="templates/style/main.css" />
<script src="templates/js/main.js"></script>
<script>var base = document.getElementsByTagName("base")[0].href;</script>
</head>
<body>
link
</body>
This way every relative link (i.e. not starting with / or a scheme) will become relative to the <base>.
This is by the way not specifically related to Tomcat in any way. It's just related to HTTP/HTML basics. You would have the same problem in every other webserver.
See also:
Browser can't access/find relative resources like CSS, images and links when calling a Servlet which forwards to a JSP
Is it recommended to use the <base> html tag?
Just use <c:url>-tag with an application context relative path.
When the value parameter starts with an /, then the tag will treat it as an application relative url, and will add the application-name to the url.
Example:
jsp:
<c:url value="/templates/style/main.css" var="mainCssUrl" />`
<link rel="stylesheet" href="${mainCssUrl}" />
...
<c:url value="/home" var="homeUrl" />`
home link
will become this html, with an domain relative url:
<link rel="stylesheet" href="/AppName/templates/style/main.css" />
...
home link
You start tomcat from some directory - which is the $cwd for tomcat. You can specify any path relative to this $cwd.
suppose you have
home
- tomcat
|_bin
- cssStore
|_file.css
And suppose you start tomcat from ~/tomcat, using the command "bin/startup.sh".
~/tomcat becomes the home directory ($cwd) for tomcat
You can access "../cssStore/file.css" from class files in your servlet now
Hope that helps, - M.S.
Instead using entire link we can make as below (solution concerns jsp files)
With JSTL we can make it like:
To link resource like css, js:
<link rel="stylesheet" href="${pageContext.request.contextPath}/style/sample.css" />
<script src="${pageContext.request.contextPath}/js/sample.js"></script>
To simply make a link:
<a id=".." class=".." href="${pageContext.request.contextPath}/jsp/sample.jsp">....</a>
It's worth to get familiar with tags
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
There is also jsp method to do it like below, but better way like above:
<link rel="stylesheet" href="<%=request.getContextPath()%>/style/sample.css" />
<script type="text/javascript" src="<%=request.getContextPath()%>/js/sample.js"></script>
To simply make a link:
<a id=".." class=".." href="<%=request.getContextPath()%>/jsp/sample.jsp">....</a>
This could be done simpler:
<base href="${pageContext.request.contextPath}/"/>
All URL will be formed without unnecessary domain:port but with application context.
This is a derivative of #Ralph suggestion that I've been using. Add the c:url to the top of your JSP.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:url value="/" var="root" />
Then just reference the root variable in your page:
<link rel="stylesheet" href="${root}templates/style/main.css">

How to add media type to stylesheet reference asp mvc

I'm using asp mvc, and I'm using the following code to generate the CSS html reference:
#Styles.Render("~/Content/css")
which generates the following html:
<link href="/Content/site.css" rel="stylesheet"/>
and that works fine. However, I need to add media type as an additional attribute. How can I go about using this style.render to add attributes to the generated html? Should I be thinking about making the change in the bundle config instead?
edit: I would like the end product to look like this:
<link href="/Content/site.css" rel="stylesheet" media="handheld"/>
You should be using #Styles.RenderFormat() for that:
#Styles.RenderFormat(#"<link href=""{0}""
rel=""stylesheet""
media=""handheld"" />",
"~/Content/css")
Try this
< link href="#Styles.Url("~/Content/css")" rel="stylesheet" type="text/css" media="handheld" />

Unable to generate a custom path in ASP.NET MVC Master Pages

I am working in an ASP.NET MasterPage and am having trouble with <link href="..." />.
I am trying to substitute in a stylesheet with a specific name:
<link href="/Content/Styles/<%=Model.Style%>.css" rel="stylesheet" type="text/css" />
Unfortunately, this creates the HTML output:
<link href="/Content/Styles/<%=Model.Style%>.css" rel="stylesheet" type="text/css" />
Which is clearly not what was intended.
If I put the same code in a View placeholder, it works perfectly. This is not a good solution though as I have many pages where I just want it to do the same thing.
It looks like it's trying to automatically correct the URL - is there a way to switch this off?
Edit 1:
I have fixed this temporarily using:
<link href=<%=String.Format("\"/Content/Styles/{0}.css\"", Model.Style)%> rel="stylesheet" type="text/css" />
All the links in your question and in the solution posted thus far will fail if your site is deployed in a virtual folder. Instead, do:
<link href="<%= Url.Content("~/Content/Styles/" + Model.Style + ".css") %>" rel="stylesheet" type="text/css" />
This (1) fixes the problem in your question, and (2) allows your site to work in a virtual folder.
Try this:
<link href="/Content/Styles/<%= "" + Model.Style%>.css" rel="stylesheet" type="text/css" media="screen" />
Ugly but works.

Paths in master pages

I've started to work a bit with master pages for an ASP.net mvc site and I've come across a question. When I link in a stylesheet on the master page it seems to update the path to the sheet correctly. That is in the code I have
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
but looking at the source once the page is fed to a browser I get
<link href="Content/Site.css" rel="stylesheet" type="text/css" />
which is perfect. However the same path translation doesn't seem to work for script files.
<script src="../../Content/menu.js" type="text/javascript"></script>
just comes out as the same thing. It still seems to work on a top level page but I suspect that is just the browser/web server correcting my error. Is there a way to get the src path to be globbed too?
<script src="<%= ResolveClientUrl("~/Content/menu.js") %>" type="text/javascript"></script>
Make an extension method. Here's a method:
public static string ResolveUrl(this HtmlHelper helper, string virtualUrl)
{
HttpContextBase ctx = helper.ViewContext.HttpContext;
string result = virtualUrl;
if (virtualUrl.StartsWith("~/"))
{
virtualUrl = virtualUrl.Remove(0, 2);
//get the site root
string siteRoot = ctx.Request.ApplicationPath;
if (!siteRoot.EndsWith("/"))
siteRoot += "/";
result = siteRoot + virtualUrl;
}
return result;
}
You can then write your script ref like:
<script type="text/javascript" src="<%= Html.ResolveUrl("~/Content/menu.js")%>"></script>
Use this instead:
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
or you can use BASE tag in you HEAD section of page. All you links then are relative to location entered in "base" tag, and you don't have to use "../../" and "~" stuff. Except links in CSS files (background url,etc), where links are relative to location of css file.

Resources