CORS (2), Consume .NET Core Web API By Angular Client in Same Origin

This is a series of articles to discuss CORS (Cross Origin Resource Sharing) issue for both setup and consuming.
This article is the second part of the previous article CORS (1), Consume .NET Core Web API By MVC in Same Origin. We will make another Web API consumer, an Angular client, and show the effect of the CORS issue.
 

Introduction

 
In the previous article, we made a Web API server and consumed it by a ASP.NET MVC Client in .NET Core in a Same Origin.
 
In this article, before we demonstrate the CORS to be enabled, we will make another Web API client by Angular.  We can see the same SORS issue here. We will make three parts in this article:
  • Build a simple Angular app to consume ag-grid data;
  • Use the Angular app to consume our previous built Web API with different origin;
  • Use the Angular app to consume our previous built Web API in the same origin, and see the difference compared to the above;

A - Build an Angular app to Consume ag-Grid data:

 
We borrow ag-Grid as a test sample to build up a Grid consuming data remotely,
  • Step 1: Create an Angular application;
  • Step 2: Add the ag-Grid NPM packages;
  • Step 3,:Add the ag-Grid styles;
  • Step 4: Update Module;
  • Step 5: Update Component;
  • Step 6: Update Template;
  • Step 7: Run and Test app
Step 1 - Create an Angular application
 
We use the Angular CLI to create projects,
  1. npm install -g @angular/cli                      // Install the Angular CLI  
  2. ng new ag-Grid --style scss --routing false      // Creat a new Angular app  
  3. cd ag-Grid                                       // change to the app directory  
  4. ng serve --open                                  // Run the app    
The app will run like this,
 
 
Step 2 - Add the ag-Grid NPM packages
 
We use the Angular CLI
  1. npm install --save ag-grid-community ag-grid-angular    
  2. npm install     
  3. npm install --save ag-grid-enterprise   
Step 3 - Add the ag-Grid styles
 
Replace the content of src/styles.scss with the following code,
@import "../node_modules/ag-grid-community/src/styles/ag-grid.scss";  
@import "../node_modules/ag-grid-community/src/styles/ag-theme-alpine/sass/ag-theme-alpine-mixin.scss";  
  
.ag-theme-alpine {  
    @include ag-theme-alpine((  
        odd-row-background-color: #CFD8DC  
    ));  
} 

Step 4 - Update Module

Replace the content of src/app/module.ts with the following code,

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';  
  
import { AppComponent } from './app.component';  
import { AgGridModule } from 'ag-grid-angular';  // ag-grid  
import { HttpClientModule } from '@angular/common/http'; // remote  
  
@NgModule({  
  declarations: [AppComponent],  
  imports: [  
    BrowserModule,  
    HttpClientModule, // remote  
    AgGridModule.withComponents([])  // ag-grid  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule {} 

Step 5 - Update Component

Replace the content of src/app/app.component.ts with the following code (the blue highlited parts will be replaced later when we use our developed Web API as server), 

import { Component, OnInit, ViewChild } from '@angular/core';  
import { HttpClient } from '@angular/common/http'; // remote  
  
import { AgGridAngular } from 'ag-grid-angular';  
import 'ag-grid-enterprise';  
  
@Component({  
    selector: 'app-root',  
    templateUrl: './app.component.html',  
    styleUrls: ['./app.component.scss']  
})  
export class AppComponent {  
    @ViewChild('agGrid') agGrid: AgGridAngular;  
    title = 'ag-Grid';  
  
    columnDefs = [  
      { field: 'make', sortable: true, filter: true },  
      { field: 'model', sortable: true, filter: true },  
      { field: 'price', sortable: true, filter: true }  
  ];  
  
  rowData: any;  
  
  constructor(private http: HttpClient) {  
  
  }  
  
  ngOnInit() {       
      this.rowData = this.http.get('https://www.ag-grid.com/example-assets/row-data.json');    
  }  
  
  getSelectedRows() {  
    const selectedNodes = this.agGrid.api.getSelectedNodes();  
    const selectedData = selectedNodes.map(node => {  
  
      return node.data;  
    });  
    const selectedDataStringPresentation = selectedData.map(node => node.make + ' ' + node.model).join(', ');  
  
    alert(`Selected nodes: ${selectedDataStringPresentation}`);  
  }  
} 

Step 6 - Update Template

Replace the content of src/app/app.component.html with the following code, 
<ag-grid-angular  
    style="width: 620px; height: 500px;"  
    class="ag-theme-alpine"  
    [rowData]="rowData | async"  
    [columnDefs]="columnDefs"  
>  
</ag-grid-angular> 

Step 7 - Run the app

We got the Grid,
 
 

B - Use the Angular Client to Consume Web API

 
We will try to use the Angular Client, built above, to consume our Web API built in the previous article. Before we make the test, we need to modify the Angular app to fit our dataset.
 
Basically, we only need to modify two major things: one is the entity class to fit our dataset, secondly, the remote endpoint of data, and we need to modify one minus: the data format.
 
In above Step 5, the Conponent file, we modify the hilighted blue parts, replace the dataset as below,
  //   columnDefs = [  
  //     { field: 'make', sortable: true, filter: true },  
  //     { field: 'model', sortable: true, filter: true },  
  //     { field: 'price', sortable: true, filter: true }  
  // ];  
  
    columnDefs = [  
    { field: 'stor_Id', sortable: true, filter: true },  
    { field: 'stor_Name', sortable: true, filter: true },  
    { field: 'stor_Address', sortable: true, filter: true },  
    { field: 'city', sortable: true, filter: true },  
    { field: 'state', sortable: true, filter: true },  
    { field: 'zip', sortable: true, filter: true },  
];  

Replace the http address as below,

// this.rowData = this.http.get('https://www.ag-grid.com/example-assets/row-data.json');        
this.rowData = this.http.get('https://localhost:44381/api/StoresWebAPI');     

And finally, in Step 6, the template file, we modify the highlighted blue format as below,

<ag-grid-angular   
<!-- style="width: 500px; height: 500px;" -->       
    style="width: 1210px; height: 200px;"    
    class="ag-theme-alpine"    
    [rowData]="rowData | async"    
    [columnDefs]="columnDefs"    
>    
</ag-grid-angular>    

Now, we can run the app, and assume we can get our Web API database back, but, we cannot: the data is loading, while never showing up,

 
This happened due to the different Origin issue: we view the web page at address: http://localhost:4200, while requesting backend data feed at port: 44381.
 
In this article, we will try to solve the problem by combining the two apps: client and server into one to make them having the same endpoint address, while in the next article, we try to enable CORS to access different origin remotely working.
 

C - Combine the Angular Client and Web API server to Make them having Same Origin

 
There are two approaches to merge the two apps together,
  •  Create an ASP.NET Core Web API app with Angular, and then insert the developed Angular app into the .NET project.
  •  Insert the Angular app into a well develped Web API server app
Approach 1 - Create an ASP.NET Core Web API app with Angular
 
For this, we still have two ways to do that:
  • Create an ASP.NET Core Web API app with Angular by Visual Studio
  • Create an ASP.NET Core Web API app with Angular by .NET Core CLI (Command Line Interface)
Way 1 - by Visual Studio
 
We use the current version of Visual Studio 2019 16.8 and .NET 5.0 SDK to build the app.
  1. Start Visual Studio and select Create a new project.
  2. In the Create a new project dialog, select ASP.NET Core Web Application > Next.
  3. In the Configure your new project dialog, enter Project name.
  4. Select Create.
  5. In the Create a new ASP.NET Core web application dialog, select,
     
    1. Select .NET Core and ASP.NET Core 5.0 in the dropdowns.
    2. Choose ASP.NET Core with Angular for the project template.
    3. Create
 
Run the app, and you will see,
 
 
Way 2 - by .NET Core CLI (Command Line Interface)
 
 see
 
 
 
 
 
 
 
Run the app from Visual Studio: Open the generated .csproj file, and run the app as normal from there. The project will look like this,
 
 
From here, we can develop the project for both Web API and Angular from scrach. Suppose, we have a well developed Angular, then we just need to replace the code in ClientApp by the developed Angular code, then modify the folder name to ClientApp.
 
Approach 2 - Insert the Angular app into a well develped Web API server app
 
For this, we use the Web API .NET Core app developed in the previous article, use Angular app develped in this article:
  • Step 1: Add the Angular app folder, ag-Grid, into the Web API project root,
  • Step 2: Change the name as "ClientApp";
  • Step 3: Modify the http address in the Angular app the same as one for Web API project --- Same Origin:
  • Step 3,:Modify the Startup.cs file to run Angular, the Single Page Application,
    app.UseSpa(spa =>  
    {  
        // To learn more about options for serving an Angular SPA from ASP.NET Core,  
        // see https://go.microsoft.com/fwlink/?linkid=864501  
      
        spa.Options.SourcePath = "ClientApp";  
      
        if (env.IsDevelopment())  
        {  
            spa.UseAngularCliServer(npmScript: "start");  
        }  
    }); 

     

 For UseSpa, we need to add the following:
 
NuGet - Solution X Browse Installed Updates Micro SO re-spa Consolidate x • Include prerelease Microsoft.AspNetCore.SpaServices.Extensions by Microsoft Helpers for building single-page applications on ASP.NET MVC Core.
 Now, run the app, we have it successfully running,
 
 

Summary

 
In this article, we made another Web API consumer, an Angular client, and showed the effect of the CORS issue. We use the same origin to solve the problem to make the combined app work in this article. In the next article, we will demostrate and analyze CORS for different situations.