13

I am trying to place my javascript files with my views.

I have the following js file location. /Views/Home/Home.js

However, when referenced with a script tag, it results in a 404 error.

As per the following SO question: ASP.NET MVC - Where do you put your .js files if you dont want to store them in /Scripts?

I added file.js to my Register routes. (Did not resolve the problem)

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{file}.js");
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    }

How can i store and reference my js files next to my views?

Community
  • 1
  • 1
Valamas
  • 24,169
  • 25
  • 107
  • 177

3 Answers3

28

The problem is that, for security reasons the web.config that is inside the Views folder blocks all request to files in that folder. This is what you will find in the config file:

<httpHandlers>
  <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

And for IIS7:

<handlers>
  <remove name="BlockViewHandler"/>
  <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>

Solution

You can change the wildcard to catch only the .cshtml files.

<httpHandlers>
  <add path="*.cshtml" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

<handlers>
  <remove name="BlockViewHandler"/>
  <add name="BlockViewHandler" path="*.cshtml" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>

With this wildcard, the .js files won't be blocked.

goenning
  • 6,514
  • 1
  • 35
  • 42
  • 1
    This works, but I think it's a better idea to not put js files in the Views folder. – ErikE Sep 27 '14 at 03:23
  • 3
    It is undoubtably safer to whitelist the files you explicitly want to allow as per the answer at http://stackoverflow.com/questions/604883/where-to-put-view-specific-javascript-files-in-an-asp-net-mvc-application rather than only filter requests for cshtml files themselves. – pwdst Dec 04 '14 at 15:38
  • 1
    For me, I've built an MVC setup that makes heavy use of angular. I have also AMD'd everything with require js so only views using certain javascript pieces cause those java script files to load. As such I compartmentalized everything and it made a lot of sense to put the angular controller associated with an mvc folder in the folder with the view. So I have something like views\ControllerName\ControllerName.cshtml and views\Controller\ControllerName-controller.js. – Ryan Mann Jun 12 '15 at 18:53
3

In my opinion, this is a good way: it keeps code simple and does not touch the security issue with other resource types.

Just add the following in the handlers section:

<add name="JavaScriptHandler" path="*.js" verb="*" preCondition="integratedMode" type="System.Web.StaticFileHandler" />
Nachokhan
  • 81
  • 7
2

I personnaly don't like to play with httpHandlers for security reasons. I wanted to do the exact same thing to prevent having to maintain the same folder structure twice (in view and in scripts folder). So, the goal is to store the .js in the same folder as my .cshtml and no longer have the 404 error.

Solution

To reach that goal, I use a custom HtmlHelper and a Controller for javascript calls.

HtmlHelper

    public static MvcHtmlString JScriptBlock<TModel>(
        this HtmlHelper<TModel> html
    )
    {
        // Get the physical path of the .js file we are looking for.
        string path = ((System.Web.Mvc.RazorView)html.ViewContext.View).ViewPath.Replace(".cshtml", ".js");
        path = HostingEnvironment.MapPath(path);

        if (!File.Exists(path))
            return null;

        // We store the physical path in a session variable with GUID as the key
        string guid = Guid.NewGuid().ToString();
        HttpContext.Current.Session[guid] = path;

        // Create the script block where the src points to the JScript controller.  We give the GUID as parameter.
        return MvcHtmlString.Create("<script src='/JScript/?id=" + guid + "'/>");

    }

JScript Controller

    public ActionResult Index(string id)
    {
        // id correspond to the guid generated by the MSRJScript helper

        // We look if the physical path of the .js is available in the session variables
        if(Session[id] == null)
            return new HttpStatusCodeResult(HttpStatusCode.Forbidden);

        // If the physical path was found, we simply send the file back to the browser.
        string path = Session[id].ToString();
        Session.Remove(id);

        return File(path, "application/javascript");
    }

Once done, you simply have to add the following code in your View/PartialView

@Html.JScriptBlock()
  • I actually like this one, but isn't this slower to get those static files since it goes through a lot of mvc invokers and methods until reaching the actual file? – Leandro Soares Jan 13 '16 at 10:14