2

I have a pure ASP.NET WebApi project (MVC 5, not .NET Core), and I want to combine it with angular-cli. Angular-cli project is in a separate directory outside of the WebApi project. ng build command puts all scripts into WebApiProject/App. I want to keep the WebApiProject as a pure WebApi (WebApiControllers only - without MVC controllers). Content, scripts and layout should be only in /App dirctory created by angular-cli.

Question: how to make it working when accessing http://localhost:5000 shows file from /app/index.html and all referenced scripts inside the /app are working.

The simple case:

WebApiProject(without MVC controllers)
--Controllers
----ValuesController
--web.config
--App
----index.html <script src="main.js" /> //src address cannot be changed, it is generated by angular-cli
----main.js
kkoziarski
  • 111
  • 3
  • 10

2 Answers2

1

Create a sub dir in your mvc app, like "Angular5".

Create your angular app here in the subfolder "Angular5" using the ng commands.

In the .angular-ci.json file edit the out dir to something like "outDir":"../MyDistFiles" This will put your distributed files outside the "Angular5" source files dir. DO NOT USE your main folder for your mvc app as the output "../" or the angular cli will delete all of your mvc app files without an undo feature so be sure to use a subdir.

Option 1: In your source files change the Index.html to

<base href="/MyDistFiles/">

This works ok if your app will be in a hybrid mobile app if the user can't select refresh on your page but if in a browser the user will get a 404 if they hit refresh.

Option 2: Create a blank MVC controller with a simple blank Index view. Call is something like MyAngularController.cs

You can use conditional compilation to use a --prod build versus debug. The prod build is 1/10 the size as the debug build, so it is worth using.

In the web project build properties, Select the release configuration. Add a conditional compilation symbol Named PROD.

public ActionResult Index()
        {
            #if PROD
              ViewBag.IsProd = true;
            #endif

            #if !PROD
              ViewBag.IsProd = false;
            #endif
            return View();
        }

In your RouteConfig.cs add this to the top (not at the bottom) in

RegisterRoutes.

routes.MapRoute(
               "Catch All",
               "MyDistFiles/{*url}",
           new { controller = "MyAngular", action = "Index", id = UrlParameter.Optional });

In the index.cshtml of MyAngularController use this.

@{
    Layout = null;
}

@if (ViewBag.IsProd)
{
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>ClientApp</title>
        <base href="~/MyDistFiles/">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
        <link href="styles.bundle.css?@ConfigSettings.AppVersion" rel="stylesheet" />
    </head>
    <body>
        <app-root></app-root>
        <script type="text/javascript" src="inline.bundle.js?@ConfigSettings.AppVersion"></script>
        <script type="text/javascript" src="polyfills.bundle.js?@ConfigSettings.AppVersion"></script>
        <script type="text/javascript" src="main.bundle.js?@ConfigSettings.AppVersion"></script>
    </body>
</html>
}


@if (!ViewBag.IsProd)
{
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>ClientApp</title>
        <base href="~/MyDistFiles/">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
    <body>
        <app-root></app-root>
        <script type="text/javascript" src="inline.bundle.js"></script>
        <script type="text/javascript" src="polyfills.bundle.js"></script>
        <script type="text/javascript" src="styles.bundle.js"></script>
        <script type="text/javascript" src="vendor.bundle.js"></script>
        <script type="text/javascript" src="main.bundle.js"></script>
    </body>
</html>
}

Now you can navigate to the http://localhost:port/MyDistFiles and use the angular app navigation, select f5 and everything reloads as it should.

When you do a prod build with angular cli it will by default add a hash to the file names making them always different for cache control. You can omit this hash action by using

ng build --prod -oh

You will notice in prod version of the cshtml I append an app version id to the javascript and css files. I do this to prevent cache of old versions in browsers. In the application properties/Assembly information/Assembly Version you can use a build number like 1.0.*.. This will create a new build number every time you rebuild.

The method in the ConfigSettings.cs class is

public static string AppVersion
        {
            get
            {
                if (string.IsNullOrEmpty(appVersion))
                {
                    appVersion = System.Reflection.Assembly.GetExecutingAssembly()
                                           .GetName()
                                           .Version
                                           .ToString();
                }
                return appVersion;


            }
        }

I like the feature of using ng serve because you can easily see your changes in seconds as you work on your angular app rather than waiting each time for a full rebuild. Unfortunately if you are not using a CORS app but maybe forms auth then you won't be able to access your web api methods from another port number. The work around for this is to open a command prompt CD to your Angular5 Dir.

Run the command ng build --watch. The --watch will do the same as ng serve except for automatically refreshing the browser but it is a lot faster and more productive to select save on your files and then f5 on the browser as a few seconds later it rebuilds that fast.

You can set up a post build command in Application/Properties/Build Events so you don't forget to build your angular if you do a prod release

cd "$(SolutionDir)MyMVCApp\Angular5"
if $(ConfigurationName) == Release (
ng build --prod -oh
) ELSE (
ng build
)
RandallTo
  • 395
  • 2
  • 11
0

If you are serving the Web API from IIS it make sense that you also serve the Angular app from IIS. You can add that using a separate MVC app, or add MVC controllers to your Web API app.

See detailed solution here: https://stackoverflow.com/a/43127291/3787891

Assaf

Community
  • 1
  • 1
Assaf S.
  • 4,676
  • 2
  • 22
  • 18