4

I have an Angular 2 application created with Angular CLI. This has to call a .NET 4.6 Web API. The route setup of this is driving me nuts.

For Angular, the default folder for output is /dist. Angular CLI does all the minification and tree-shaking you can dream of and then outputs both its JavaScript files and index.html to that folder.

So if I run index.html, those files are retrieved from the same folder. Everything works well, because index.html contains a tag like this one:

<base href="/">

The Web API project has the expected structure, with the Angular app a parasite in its root.

So it looks like this:

WebAPI_Project
|- App_Start
| |
| |-WebApiConfig.cs
|- Controllers
|- Models
|- src /* This is the root of the Angular app */
| |- app
| | |- core /* These three folders */
| | |- shared /* are for the modules */
| | |- another_module /* used in the app */
| | |- app.component.ts
| | |- app.module.ts
| |- index.html
|- dist /* This is the Angular output folder */
 |- index.html
 |- main.bundle.js

WebApiConfig.cs has the default setup:

public static void Register(HttpConfiguration config)
{
 // Web API routes
 config.MapHttpAttributeRoutes();
 config.Routes.MapHttpRoute(
 name: "DefaultApi",
 routeTemplate: "api/{controller}/{id}",
 defaults: new { id = RouteParameter.Optional }
 );
}

To run the Angular app, I use normal ng serve, which create a web server at http://localhost:4200. This web server knows nothing about the Web API project, so I need to run that as well from within Visual Studio, which starts up IIS Express at http://localhost:5200.

This setup works reasonably well and allows me to take advantage of Angular CLI's live reload support.

The biggest drawback for me is that this configuration is different from what we expect for production. There, I would expect to have one web server (IIS), serving both the Web API project (at /api/ as presently) and the Angular app (at /, preferably). Also, in dev, I'm having to think of CORS, which the production setup wouldn't.

To do this, I need to change the WebApiConfig routing to serve my static files, which will be under /dist/.

For ASP.NET Core and for MVC 4, there are numerous articles showing how I should use Microsoft.AspNetCore.StaticFiles (or Microsoft.AspNet.StaticFiles) in Startup.cs. Basically, add the following two lines to the Configure function:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
 ILoggerFactory loggerFactory)
{
 // Other stuff
 // Two new lines
 app.UseStaticFiles();
 app.UseDefaultFiles();
 app.UseMvc(m =>
 {
 // Other routes specified here
 });
}

The problem is that I don't use either ASP.NET Core or MVC.

Despite Web API apparently being MVC without the views, a Web API project comes with only the WebApiConfig.cs file, no Startup.cs and no `IApplicationBuilder'.

I tried adding this line to my Register function:

config.Routes.IgnoreRoute("StaticFiles", "*.html");

This serves the index.html (but at localhost:5200/dist/index.html), but it cannot find any of its assets, because base href="/". I can change that in index.html, but then ng serve breaks.

I think I need one of two things:

  1. A way to create a route so a reference to /index.html serves /dist/index.html, or
  2. A way to use the StaticFiles assembly mentioned before.

So how would I accomplish that?

asked Jan 26, 2017 at 14:10

3 Answers 3

4

You need first disable web api routing for angular files, add this to web.config, inside <system.webServer> </system.webServer>:

<rewrite>
 <rules>
 <rule name="AngularJS" stopProcessing="true">
 <match url=".*" />
 <conditions logicalGrouping="MatchAll">
 <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
 <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
 <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
 </conditions>
 <action type="Rewrite" url="/index.html" />
 </rule>
 </rules>
</rewrite>

And then, add <base href="/"> to your index.html.

UPD: Rewrite access to index.html

You can add another rewrite rule to override access to your /dist/ folder:

<rewrite>
 <rules>
 <rule name="DistAccess" stopProcessing="true">
 <match url="^(.*)/dist/(.*)" />
 <action type="Rewrite" url="{R:1}/{R:3}" />
 </rule>
 </rules>
</rewrite>
Bakudan
19.5k9 gold badges55 silver badges76 bronze badges
answered Jan 26, 2017 at 14:22
8
  • Great! I can now serve my Angular app from either Visual Studio or ng serve. Is there a way for me to rewrite it so that index.html is at /, not /dist/? Commented Jan 26, 2017 at 14:53
  • I completely dont understand what u need, if you need to access your app like yourdomain.com/some-angular-url you need to change rewrite action to<action type="Rewrite" url="/dist/index.html" />, but if you want to store your index.html out of dist folder, you need to work with your module bindler Commented Jan 26, 2017 at 15:01
  • The HTML and JS files are all under /dist/. I want them to be served at /, so if a user loads the site at www.something.com/, he'll be served index.html, which should have access to all its script files. Commented Jan 26, 2017 at 15:13
  • If you link your action to /dist/index.html you will get same result, but only data will be stored inside /dist/, but users would see www.something.com/ as well Commented Jan 26, 2017 at 15:16
  • We're missing each other somewhere. Using your answer, I can still only access the app at /dist/index.html, the address bar reflects /dist/ and the resources cannot be loaded (base href = "/"). What I want is for the address bar to display /, /dist/index.html to be served. Its JS files to be accessed using /script.js but actually served from /dist/script.js. Commented Jan 26, 2017 at 15:29
2

I just couldn't get Dmitriy's answer to work for me, but he did put me on the right track.

I needed two rules: One to serve the Angular app from the root while maintaining the Web API /api/ routing, and another to serve the default document.

Here they are:

<rewrite>
 <rules>
 <rule name="Serve Angular app from root" stopProcessing="true">
 <match url="([\w\.]+)" />
 <conditions>
 <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
 </conditions>
 <action type="Rewrite" url="/dist/{R:1}" />
 </rule>
 <rule name="Default document" stopProcessing="true">
 <match url="^$" />
 <action type="Rewrite" url="/dist/index.html" />
 </rule>
 </rules>
</rewrite>
Bakudan
19.5k9 gold badges55 silver badges76 bronze badges
answered Jan 27, 2017 at 7:15
3
  • I have a similar situation. Adding this to web.config file doesn't seem to work for me. I added this to the <system.webServer> section Commented Jun 13, 2017 at 14:58
  • This worked for me except that I deployed angular 2 files in root itself so I removed /dist from your example. Thanks. Commented Sep 14, 2017 at 13:27
  • This works when used a domain name. If we have route in the URL and paste it on the address bar it still goes for a search of an API instead of loading index.html with route parameters Commented Jun 12, 2018 at 6:00
0

First of all you should publish your angular project like this below.

ng build --prod="true" --base-href /

Then add web config file to your angular project .

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
 <rules>
 <rule name="AngularJS Routes" stopProcessing="true">
 <match url=".*" />
 <conditions logicalGrouping="MatchAll">
 <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
 <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
 <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
 </conditions>
 <action type="Rewrite" url="/" />
 </rule>
 </rules>
</rewrite>
</system.webServer>
</configuration>

notice url in <action type="Rewrite" url="/" /> is that url in --base-href / .

answered Mar 2, 2021 at 8:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.