This is a series of articles to discuss CORS (Cross Origin Resource Sharing) issue for both setup and consuming.
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,
- npm install -g @angular/cli // Install the Angular CLI
- ng new ag-Grid --style scss --routing false // Creat a new Angular app
- cd ag-Grid // change to the app directory
- 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
- npm install --save ag-grid-community ag-grid-angular
- npm install
- 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.
- Start Visual Studio and select Create a new project.
- In the Create a new project dialog, select ASP.NET Core Web Application > Next.
- In the Configure your new project dialog, enter Project name.
- Select Create.
- In the Create a new ASP.NET Core web application dialog, select,
- Select .NET Core and ASP.NET Core 5.0 in the dropdowns.
- Choose ASP.NET Core with Angular for the project template.
- Create
Run the app, and you will see,
Way 2 - by .NET Core CLI (Command Line Interface)
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:
For UseSpa, we need to add the following:
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.