Routing an Angular 2 app with Web API

2020-06-05 06:21发布

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?

2条回答
神经病院院长
2楼-- · 2020-06-05 06:48

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>
查看更多
beautiful°
3楼-- · 2020-06-05 06:56

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>
查看更多
登录 后发表回答