You would think that creating an ASP.NET Core application that uses Angular would be easy-peasy. After all, there’s an “Angular” option in the new project wizard for an ASP.NET Core application! Sadly, reality is a cruel mistress, and if you take that approach to creating your next Angular + ASP.NET Core project, you’ll be in for a world of hurt. Here’s a better approach.
Please note: This article was written for the .NET Core 2.0 project templates. The new .NET 2.1 templates, currently in preview, change things up for the better. Here’s an overview of the new template, including some improvements!
Credit where credit is due: this post was inspired by Levi Fuller’s take on the topic. I made a few changes along the way, but his approach got me started!
Friends Don’t Let Friends Use File -> New Project -> Angular
Let’s get this out of the way: yes, there is an easy-to-use, File -> New Project way to make an ASP.NET Core application with Angular. But you don’t want to use that.
This will give you an Angular 4x project built directly around Webpack. For reference, Angular 5 is the current production version of Angular, and Angular 6 is approaching release. Heck, there’s a good chance Angular 6 will be out before I get around to publishing this post!
But the out-of-date version of Angular isn’t the biggest reason you shouldn’t use the Visual Studio project template. The biggest is that it does not use the Angular CLI. Instead, it uses webpack directly. You don’t want that. You want to use the Angular CLI. It is the bee’s knees. Why?
Quite simply, because using it means you no longer need to manage your own build pipeline. And if that isn’t enough for you, it has numerous other handy capabilities, such as generating (and wiring up!) new components, producing optimized builds, serving content with hot reloading…
If you’re doing Angular development, you want to use the CLI!
And, aside from even that, the Visual Studio Angular template gives you a lot of junk you probably don’t need or want, such as the Razor view engine, a home controller, a (poorly) implemented starter project, etc.
So what do we do instead? Well, it turns out it’s remarkably easy to add Angular to an existing ASP.NET Core app using the CLI. Let’s try it out!
Creating a New Project
We are still going to start with a new ASP.NET Core project in Visual Studio. Choose the ASP.NET Core Web Application option…
On the next screen, choose to create a Web API project, not an Angular one!
Now we can fire up the command prompt (I use Hyper with Powershell, by the way), make sure we have the latest-and-greatest Angular CLI installed…
npm i -g @angular/cli
Now we are ready to create our new Angular project!
Warning: There’s a bug in the current version of Angular CLI that prevents you from creating a project inside a non-empty directory. We’re going to work around that, but in the future, this step shouldn’t be necessary!
We’ll use the ng new
command to create our project.
Make sure you’re in the same directory as your new ASP.NET Core project (not the solution!), and execute the following command:
ng new my-project --skip-install
Want to start with routing enabled? Or would you rather use LESS or SASS instead of normal CSS? The
ng new
command has a lot of useful options. Check out the documentation for more information!
This will create our new project in a subfolder of our ASP.NET Core project.
Now we need to move those files up to the root of our project. This PowerShell command will do that and clean up the now-empty folder:
mv .\my-project\* .
rm .\my-project\
Now we just need to restore our npm packages.
I’ve had mixed results doing this initial restore from the command-line while Visual Studio is running. It seems like Visual Studio, somehow, interferes with things, leading to strange errors and a corrupted node_modules
folder.
So instead, you should restore the packages by right-clicking on the npm folder under your project’s dependencies, and selecting “Restore Packages.”
Reconfiguring the Angular CLI
At this point, we have our ASP.NET Core app, and we have our Angular CLI app living alongside it. But we need to reconfigure things a bit to make them play nicely together.
See that .angular-cli.json
file in the root of your solution? We can modify that file to change how the Angular CLI behaves.
By default, if we create a production build of our client-side app, the CLI will create a dist
folder with our bundled JS, CSS, and HTML. That’s not what we want.
We want our files to go to the wwwroot
folder that ASP.NET Core serves static files from. All we need to do is find the outDir
setting and change it from dist
to wwwroot
:
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "my-project"
},
"apps": [
{
"root": "src",
"outDir": "wwwroot",
//snip!
],
//snip!
}
Now when we execute ng build
, our Angular app will be output to the wwwroot
folder, ready for publishing!
Running Locally
I’m a huge fan of eliminating as much friction as possible from your day-to-day development workflow. Unfortunately, this setup that we have now? It’s not going to work all that well for our day-to-day.
When we want to run our app locally, we’re going to need to do a few different things:
- Build and run our ASP.NET Core project.
- Start our Angular app by executing
ng serve
. - Make sure our Angular app executes API calls against our locally-running ASP.NET Core project
Compare that to a “normal” ASP.NET Core application with MVC, where the workflow is basically:
- Press F5.
But don’t worry! We can fix this!
Leveraging Task Runner Explorer
The first thing we’ll do is configure our Angular application to be served immediately when we open our project. We can do that with Task Runner Explorer.
If you don’t have the Task Runner Explorer window visible already, you can show it by searching for Task Runner
in the Visual Studio Quick Launch box:
Using Task Runner Explorer, right-click on the start
task, go to Bindings
, and set it to Project Open
.
That will instruct Task Runner Explorer to run the start
task immediately whenever you open the project.
Let’s improve on this just a bit. Let’s make the start
task also open our Angular application in a new browser window.
In package.json
, find the start
task, and add the --open
argument, like so:
{
"name": "my-project",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
//SNIP!
},
//SNIP!
}
Now we just need to make sure our Angular application calls our ASP.NET Core APIs.
Proxying Requests from Angular to ASP.NET Core
When we deploy this application to production, our Angular app and our ASP.NET Core Web APIs will be running on the same host.
We’ll load our Angular app by navigating to https://some.url/
, and it will make calls to https://some.url/api/some/endpoint
.
But when we’re running locally, we actually want our Angular app running on one host, and ASP.NET Core on another! That’s because the Angular CLI will serve our Angular app (with hot-reloading!) for us! And we get the same thing on the ASP.NET Core side from Visual Studio and dotnet run
!
That means our Angular app and our ASP.NET Core app won’t be running on the same host. Our Angular app will be running on something like http://localhost:4200/
, and our ASP.NET Core app will be running on http://localhost:56221
.
There are a couple of ways to handle that.
One way is to use a proxy. That’s the approach Levi Fuller took .
If we were going to do that, we’d create a proxy configuration file, like so:
{
"/api": {
"target": "http://localhost:56221/api",
"secure": false,
"pathRewrite": {
"^/api": ""
}
}
}
Then we’d run ng serve
using its proxy parameter: ng serve --proxy-config proxy.config.json
.
And that will work, but that’s not the option I like to use.
Leveraging Environment Configuration Files
Instead of using Angular’s proxy configuration, I prefer to use the built-in support for environments. This accomplishes the same thing while giving me flexibility to easily move my API somewhere external in the future, even in production, should the need arise.
My default environment file looks like this:
export const environment = {
production: false,
apiUrl: 'http://localhost:56221/api'
};
Now I can use environment.apiUrl
when I’m building up API requests.
I like to take things one step further though, and wrap Angular’s HttpClient
with a custom service that will apply this prefix automatically. Here’s my ApiClient
service:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { environment } from '../environments/environment';
@Injectable()
export class ApiClient {
constructor(private httpClient: HttpClient) {}
private getUrl(url: string) {
const trimmedUrl = url.startsWith('/') ? url.substring(1) : url;
return `${environment.apiUrl}/${trimmedUrl}`;
}
public post<T>(
//snip
) {
return this.httpClient.post<T>(this.getUrl(url), body, options);
}
public get<T>(
//snip!
) {
return this.httpClient.get<T>(this.getUrl(url), options);
}
public put<T>(
//snip!
) {
return this.httpClient.put<T>(this.getUrl(url), body, options);
}
}
The argument definitions for
post
,get
, andput
are quite lengthy. You can see the full implementation in the Github repo.
ApiClient
just wraps HttpClient
and converts relative URLs, like /SomeEntity
and SomeEntity/Action
into URLs that target the API: http://localhost:56221/api/SomeEntity
and http://localhost:56221/api/SomeEntity/Action
respectively.
Here’s an example of how to use the client:
async ngOnInit() {
this.response = await this.apiClient.get<any>('Values').toPromise();
}
You do need to make one more change to your ASP.NET Core application, too.
Enabling CORS
The out-of-the-box ASP.NET Core Web API project only allows requests from the same origin. We need to enable cross-origin requests.
In Startup.cs
, add a call to AddCors()
to your ConfigureServices
method:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCors();
}
And add a call to UseCors()
following to your Configure
method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseMvc();
}
The order that middleware is added matters. Be sure you add UseCors()
BEFORE the call to UseMvc()
. If you don’t, the MVC middleware will reject the request before the CORS middleware has a chance to do anything.
The Code
So there you have it: an ASP.NET Core application with an Angular CLI app embedded in it. This is better than using the ASP.NET Core 2.0 Angular template, which sets you up with an old version of Angular and an obsolete build system.
If you want to check out the code, head on over to the Github repository. Let me know what you think!
Good read ! Just curious if it has to be ASP.NET Core or can the similar steps be followed for the older versions of ASP.NET ?
Good question! The general approach would work, especially for local development. Deploying would be a bit more of a challenge though. The problem with the older version of ASP.NET is that files are explicitly added to the project file, and every time you run `ng build`, the resulting files will be hashed. There’s a way around that though, which I’ll try to cover in a future post.
Thank you for the detailed article. Angular CLI is now supported in Vs2017, check this link out – https://darenmay.com/blog/angular-cli-support-in-vs-2017/
Hi Surabh! You are correct, the new 2.1 preview template does use the CLI. I actually have an article queued up (probably will go live today) about that template, and about how to improve it. 🙂
Download angular templates for visual studio, using systemjs and gulp:
https://asptemplatestack.com/Templates/GetAll?id=17