I have been stuck with an issue exchanging data from an Angular 9 UI web app. I know there plenty of topics on the area, but I cannot make it work, I will share my code with you to see if I can get there. I cannot seem to find where the root-cause is, if its .Net API configuration error, NGINX, or the Angular app. This is my first "real" solution, so I am still learning my way around all the components.
I have a .Net 3.1 API one controller, exposing MySQL Db data. The Angular app is behind an NGIX Proxy server deployment.
GET, PUT, POST, DELETE requests work from Postman app or from navigating to the controller address. Also, the GET request works in the Angular app. However, PUT, POST request does not work.
I have tried without success to modify the .NET API Startup configuration adding the CORS Nugget package from Microsoft following their tutorial. After doing this the GET stops working too. Error in Angular App when performing a PUT / POST request:
Access to XMLHttpRequest at 'http://192.168.10.40/api/AlertsConfig/' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I tried:
public void ConfigureServices(IServiceCollection services)
{
// ...
// Add CORS policy
services.AddCors(options =>
{
options.AddPolicy("test",
builder =>
{
// Not a permanent solution, but just trying to isolate the problem
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
// Use the CORS policy
app.UseCors("test");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
My .Net Controller - For making it simple I am just interested in PUT
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BeckerSmartFan.Data;
using BeckerSmartFan.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace BeckerSmartFan.Controllers
{
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class AlertsConfigController : ControllerBase
{
private readonly ConnectionStrings con;
private readonly Data.MySqlConnector mySqlConnector;
public AlertsConfigController(ConnectionStrings c)
{
con = c;
mySqlConnector = new Data.MySqlConnector();
}
// GET: api/AlertsConfig
[HttpGet]
public async Task<IActionResult> Get([FromQuery] IEnumerable<AlertsConfig> alertsConfigs, [FromQuery] string type)
{
return await Task.Run(() =>
{
var listAlertConfigs = mySqlConnector.GetAllAlertsConfigs(con, type);
return listAlertConfigs != null ? (IActionResult)Ok(listAlertConfigs) : NotFound();
});
}
// PUT: api/AlertsConfig/
[HttpPut]
public async Task<IActionResult> Put([FromBody] AlertsConfig alertConfig)
{
return await Task.Run(() =>
{
var result = mySqlConnector.UpdateAlertConfig(con, alertConfig);
return result > 0 ? (IActionResult)Ok(alertConfig) : BadRequest();
});
}
}
}
My Angular Http Service:
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { catchError, map, timeout } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { NotFoundError } from '../common/not-found-error';
import { FanDataTag } from './FanDataTag';
const QueryTimeOut : number = 5000;
export class ApiDataService {
constructor(private url: string, private http: HttpClient) {
console.log("Contstructor:" + url);
}
getAll(startDate?:string, endDate?:string){
if(typeof startDate === 'undefined' && typeof endDate === 'undefined')
{
return this.http.
get<any[]>
(
this.url
)
.pipe
(
catchError
(
this.handleError
)
,
timeout
(
QueryTimeOut
)
);
}
else
{
return this.http
.get<any[]>
(
this.url,
{
params:
{
dateStart:startDate,
dateEnd: endDate
}
}
)
.pipe
(
catchError
(
this.handleError
),
timeout(
QueryTimeOut
)
);
}
}
getAlertsConfig(type?:string){
if(typeof type === 'undefined')
{
return this.http.
get<any[]>
(
this.url
)
.pipe
(
catchError
(
this.handleError
)
,
timeout
(
QueryTimeOut
)
);
}
else
{
return this.http
.get<any[]>
(
this.url,
{
params:
{
type:type
}
}
)
.pipe
(
catchError
(
this.handleError
),
timeout(
QueryTimeOut
)
);
}
}
getLastRecord(getLastRecord?:boolean)
{
return this.http.get<any[]>(this.url, { params:{
dateStart:null,
dateEnd: null, getLastRecord: getLastRecord.toString()} }).pipe(timeout(QueryTimeOut));
}
getRecord(recordId?:number)
{
return this.http.get<any[]>(this.url + "/" + recordId).pipe(timeout(QueryTimeOut));;
}
create(resource: any){
return this.http.post<any>(this.url , JSON.stringify(resource))
.pipe(
catchError(this.handleError));
}
update(resource:any, isPatch:boolean)
{
if(isPatch)
{
return this.http.patch(this.url, JSON.stringify({isRead:true}))
.pipe(
catchError(this.handleError));
}
else
{
return this.http.put(this.url, resource );
}
}
delete(id: number){
return this.http.delete(this.url+"/"+id)
.pipe(
catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
console.log(error);
if (error.error instanceof NotFoundError) {
// A client-side or network error occurred. Handle it accordingly.
return throwError(
'Bad request. Please refresh your page, and try again.');
}
else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
return throwError(
error.statusText + ": " + error.name );
}
};
}
NGINX Configuration:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/WebApp;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Content-Type,Accept';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
proxy_pass http://localhost:5000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/FanDataTags {
proxy_pass http://localhost:5000/api/FanDataTags;
}
location /api/AlertsConfig {
proxy_pass http://localhost:5000/api/AlertsConfig;
}
server {
listen 8080;
listen [::]:8080;
server_name _;
root /var/www/Angular;
location / {
#directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html;
index index.html index.htm;
#proxy_pass http://localhost:8080/;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection keep-alive;
#proxy_set_header Host $host;
#proxy_cache_bypass $http_upgrade;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header X-Forwarded-Proto $scheme;
}
}
