0

I'm working on a project where i want to build a REST Api and an angular2 Frontend in one project with VS2015 as a MVC/Webapi Project.


Project settings and folder structure:

In my VS solution i created a new ASP.NET Web Application project using following settings:

project setup

In the root of my project i added a new folder called Public where the whole frontend source will be. For my angular2 Frontend i'm using angular/angular2-seed as a starting point.

In my Public Folder the actual webapp will therefore be in another folder called dist and might look like this:

frontend folder structure

while my VS solution looks like this:

solution folder structure


My Goal:

Now, when running an IIS server at http://localhost/ i want to achieve that

  • my REST Api will be targeted at http://localhost/api/... (just for information, not really a problem at this point)
  • every other route will be targeting at my index.html located in the folder /Public/dist/

now to the tricky parts:

  • i want the URL to be pretty, so in my browsers addressbar it should show http://localhost/ while every other segment should be handled by the router of my angular2 application. For example i have a component dashboard so http://localhost/dashboard should be routed to my index.html file by IIS where angular2 router will kick in to show the corresponding .html file of the component.
  • i don't want to relocate parts of my frontend code / markup to another file (for example to create a Index.cshtml in Views/Home or something like that
  • at last, i don't want the frontend developer (me ^^) ever worry again about where my app is put in whatever envorinment which means i don't want to touch the <base href> tag in my index.html which should always look like this <base href="/">. I don't know if this is possible, but i definitely don't want to put /public/dist/ there.

What i've tried so far:

Now just starting the project would end up in a messed up behaviour, because i had to navigate manually to http://localhost/public/dist/ which will then open my app and navigate to something like http://localhost/public/dist/dashboard. A refresh would break it, cause this route wouldn't be found.

First real approach was to use url mapping as suggested here in an answer: How can I configure a route for WebApiConfig to redirect some requests to static html page?

<urlMappings enabled="true">
  <add url="~/" mappedUrl="~/Public/dist/index.html" />
</urlMappings>

Well, didn't work. Okay it routed to the app by itself, but the URL still looked ugly and some more stuff was broken.

After that i tried the RouteConfig which looked then like this:

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

        routes.MapRoute(
            name: "publicOverride",
            url: "{*.}",
            defaults: new { controller = "Public", action = "Index" }
        );
    }
}

In my PublicController i tried several methods to return my index.html for example

public ActionResult Index()
{
    var result = new FilePathResult("~/Public/dist/index.html", "text/html");
    return result;
}

This would work if i change the <base> tag to <base href="/Public/dist/"> which i don't want and the url would still be http://localhost/Public/dist/dashboard. At least, in this case, refreshing would work.

I tried to change the site options in my IIS directly to point at public/dist for this case, but this would break again the app regarding refreshing and navigating directly to something like http://localhost/dashboard doesn't work as well.

After that i played around with several other things which could have helped, but actually didn't. For example in this question: How to route EVERYTHING other than Web API to /index.html i tried all answers, and the solution for the OP (in the edit section) as well. Yelling, throwing away computers, smashing in keyboards and screens didn't help either.

Any way to accomplish this?


**EDIT - Current Solution **

I've duplicated my index.html as Index.cshtml (in a Views/Home/ Folder), corrected the paths (so that each script included and the base tag has /Public/dist/ at the start) and created a HomeController which will return this View. In my angular2 project i set the base tag to <base href="/public/dist">, so manipulating the index.html / .cshtml is something i have to live with, i guess.

Now an important step was to add APP_BASE_HREF to the angular2 bootstrap function as well but with / as the value:

bootstrap(AppComponent, [
    HTTP_PROVIDERS, 
    ROUTER_PROVIDERS,
    provide(APP_BASE_HREF, { useValue: '/' })
]).catch(err => console.error(err));

This will get rid of the behaviour that all my urls started with http://localhost/public/dist/... instead of starting at http://localhost/.

Community
  • 1
  • 1
malifa
  • 8,025
  • 2
  • 42
  • 57
  • Unsure if I got things so if not, disregard. If you want ASP.net to handle routing, then let it - `/foo/bar/something.cshtml` (not a typo - `cshtml`) can be "pretty-fied" `with attribute routing`. I guess what I'm saying is that let ASP.Net handle what it can and Angular do it's thing at the point it "takes over" (with its own _client side_ routing). So '`something.cshtml` is that "starting point" for Angular. Hope this make sense.. – EdSF Apr 18 '16 at 18:04
  • That won't help for the behaviour i'm asking. Maybe, maybe i could live with it, if this wouldn't be so easy to achieve in any other stack i worked with (MEAN, wamp/lamp (with laravel or a pure php project using htaccess etc.). It has to work somehow in the VS / IIS environment. – malifa Apr 19 '16 at 16:40

1 Answers1

0

You'll probably want to add a url rewrite rule to route request to everything to your index.html except for webapi requests. Add something like this to your web.config:

<system.webServer>
  <rewrite>
    <rules>
      <rule name="RewriteUnknownToIndex" patternSyntax="ECMAScript" stopProcessing="true">
        <match url="^(public|api)/.*" negate="true"/>
        <action type="Rewrite" url="/public/dist/index.html"/>
      </rule>
    </rules>
  </rewrite>
</system.webServer>

For more information, see Url rewrite for ASP.NET Web API SPA.

Douglas Ludlow
  • 10,754
  • 6
  • 30
  • 54
  • Doesn't work for me. When i look at the sources tab in my chrome developer tools it has the content of the index.html in every .js file (which is weird) and doesn't load my css files. – malifa Apr 18 '16 at 18:14
  • @lexith every .js file is being rewritten index.html because the scripts are being loaded relative to root. For example in the index.html, the script src for app.bundle.js and so the server is requesting /app.bundle.js. If you add to `/public/dist/` to the scripts path, it will load it correctly. – Douglas Ludlow Apr 18 '16 at 19:08
  • But that's what i don't want. For example i could take my frontend app and let it run as it is under a node server. i think it wouldn't be a problem to let it run under my apache with this behaviour as well and i wouldn't need to touch my relative paths. Basically that' what i want. I want to host my frontend app under root, and every other request which isn't a directory matching should be routed elsewhere (for example my api). But it doesn't seem to work that way with a subfolder like Public. Otherwise it's no problem getting it to work. – malifa Apr 18 '16 at 20:17