Can't call controller from angular component - c#

I'm trying Angular with .Net Core and I get trouble calling api controller from my components:
I called my controller with Postman with fake data return to check wether it's my app or my controller and everything is ok with my controller, so the problem lays in my app.
I spent many hours on this trying many things (creating another "service" component or using httpclient straight in my component, in the constructor, in ngInit, outside both, etc etc....) and now I'm really stuck... On all httpClient examples I can find, nothing tell me my call si wrong so I don't understand...
When I try to debug (source explorer on chrome) I can reach the httpClient call, the url is good (same as postman) but then it's the black box, only anonymous functions but I can see my "userlist" is still undefined after the http.get().subsrcibe();
startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.RegisterServiceServices();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseExceptionHandler("/default");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
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");
}
});
loggerFactory.AddFile("logs/Log_" + DateTime.Today.ToString()+".txt");
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { HttpClientModule } from '#angular/common/http';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component';
import { HomeComponent } from './content/home/home.component';
#NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
HomeComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
HttpClientModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
home.component.ts
import { Component, Injectable } from '#angular/core';
import { user } from '../../../model/test/user';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'home',
templateUrl: './home.component.html',
})
#Injectable()
export class HomeComponent {
public userlist: user[];
constructor(private httpClient: HttpClient) {
var base_url: string = window.location.origin;
var controller: string = base_url + "/svc/Test/GetUserList";
var userlistresult = this.httpClient.get<user[]>(controller).subscribe((data: any[]) => {
console.log(data);
this.userlist = data;
});
}
}
user.ts //used the same case as the .cs class to be sure that's not the reason
export interface user {
UserName: string;
FirstName: string;
LastName: string;
}
TestController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Business.Interfaces;
using Newtonsoft.Json;
using Models.Test;
using Microsoft.AspNetCore.Mvc;
namespace AnguTest.Controllers
{
[ApiController]
[Route("svc/Test")]
public class TestController : ControllerBase
{
#region Attributes
private ITestService TestService;
#endregion
#region Constructor
public TestController(ITestService testService)
{
TestService = testService;
}
#endregion
#region Methods
[HttpGet]
[Route("GetUserList")]
public JsonResult GetUserList()
{
User[] totos = new User[]
{
new User(){UserName = "toto1", FirstName = "tata1", LastName = "titi1"},
new User(){UserName = "toto2", FirstName = "tata2", LastName = "titi2"},
new User(){UserName = "toto3", FirstName = "tata3", LastName = "titi3"}
};
return new JsonResult(totos);
//return new JsonResult(TestService.GetUserList());
}
#endregion
}
}
Any help is welcome :)

Related

Angular client, WebAPI server - cookie issue when userManager.GetUserAsync() method

I'm preparing WebAPI where the client (Angular) is asking via HTTP for logging in and current user.It works fine, when I'm sending POST and GET requests from Swagger (works on https://localhost:44322/swagger/index.html). I receive all necessary answers, but fun thing happens when I'm trying to do so from Angular (works on https://localhost:4200). CORS origin turned on, headers allowed, any method allowed, credentials allowed...
I think I run into a cookie-related issue, because, when I open both cards (swagger and angula) in the same browser window, I'm able to do everything find, but when I separate them, swagger works, but Angular stop seeing cookies which come from the server-side.
I think I tried everything. I tried to play withCredentials paremeter in HTTP requests, I tried to parametrize CORS to allow switch on AllowCredentials(); method. Nothing worked.
So, Swagger can send requests like below.
I also implemented HTTP requests from Angular.
Below login.component.ts
import { HttpClient } from '#angular/common/http';
import { Message } from '#angular/compiler/src/i18n/i18n_ast';
import { Component, OnInit } from '#angular/core';
import { first } from 'rxjs';
import { UserService } from '../user.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
response: any;
currentUser = {
firstName: "",
lastName: ""
};
user: any;
userLogin = {
email: "",
password: ""
}
firstName: string = "";
lastName: string = "";
constructor(private http: HttpClient, private service: UserService) { }
ngOnInit(): void {
this.getCurrentUser();
}
loginAction(): any {
this.response = this.service.loginUser(this.userLogin);
if(this.response){
this.service.currentUser().subscribe((response: any) =>{
this.currentUser.firstName = (response as any).firstName;
});
}
}
logoutAction():any{
this.service.logoutUser();
}
getCurrentUser(){
this.service.currentUser().subscribe((response: any) =>{
this.currentUser.firstName = (response as any).firstName;
});
}
}
And user.service.ts
export class UserService {
readonly taskAPIUrl = "https://localhost:44322/api";
constructor(private http: HttpClient) { }
loginUser(userLogin :any) {
return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe();
}
logoutUser(): any {
return this.http.post<any>("https://localhost:44322/api/UserLogin/logout", {withCredentials: true}).subscribe();
}
currentUser(): any {
return this.http.get<any>("https://localhost:44322/api/UserLogin/getCurrentUser", {withCredentials: true});
}
Here is Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ToDoListAPI.Data;
using ToDoListAPI.Models;
namespace ToDoListAPI
{
public class Startup
{
private string myAllowSpecificOrigins = "_myAllowSpecificOrigins";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ToDoListAPI", Version = "v1" });
});
services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("ConnectionString"));
});
//Enable CORS
services.AddCors(options =>
{
options.AddPolicy(name: myAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://localhost:4200").
AllowAnyMethod().
AllowAnyHeader().
AllowCredentials();
});
});
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.ClaimsIdentity.UserNameClaimType = "UserID";
}).
AddEntityFrameworkStores<DataContext>().
AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = false;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ToDoListAPI v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(myAllowSpecificOrigins);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
UserLoginController.cs where I send HTTP requests
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using ToDoListAPI.Models;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace ToDoListAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserLoginController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public UserLoginController(UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
// GET: api/<UserLoginController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<UserLoginController>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
[HttpGet]
[Route("getCurrentUser")]
public async Task<IActionResult> GetCurrentUser()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Unauthorized();
}
return Ok(user);
}
// POST api/<UserLoginController>
[HttpPost]
public async Task<IActionResult> Login([FromBody] UserLogin userLoginDto)
{
var foundUser = await _userManager.FindByEmailAsync(userLoginDto.Email);
if (foundUser == null)
{
return NotFound();
}
var result = await _signInManager.PasswordSignInAsync(
foundUser, userLoginDto.Password, true, false);
if (result.Succeeded)
{
return Ok();
}
return NotFound();
}
// POST api/<UserLoginController>
// in progress
[HttpPost]
[Route("logout")]
public async void Logout()
{
await _signInManager.SignOutAsync();
}
// DELETE api/<UserLoginController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
Please help, I think I stuck somewhere...
Here is example of UserLogin request from Swagger
And here from angular client
As you can see, Swagger has a lot more in the request and response stay the same. The biggest problem is when I send getCurrentUser() request.
Swagger:
and angular
Ok. For angular it should look something like this.
In user.service.ts methods should return Observalbe.
For an example:
loginUser(userLogin : "here should be model class): Observable<Any> {
return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe(repond => {return respond});
return this.httpClient
.post("https://localhost:44322/api/UserLogin",userLogin)
.pipe(map(resposne =>{
return resposne;
}),
catchError(error => {
console.log(error);
}));
}
In login.component.ts login should look something like this:
loginAction() {
this.service.loginUser(this.userLogin)
.pipe(first())
.subscribe( response =>{
this.currentUser.firstName = response.firstName;
}, error => {
console.log(error);
});
}
For GetCurrentUser in Controller file try tu parse tu yours id type instead of User this User.Identity.Name or User.Identity.Id

Connection problems between Asp.Net Core Api and Asp.Net Webapp with Angular

I have a web api with asp.net core and an Asp.Net core webapp in which communication between them is not happening.
I did the api tests with Postman and everything worked fine, but when it's the web application this error is being returned:
Access to XMLHttpRequest at 'http://localhost:15741/api/Home/CalculaEmprestimo' from origin 'http://localhost:39903' 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.
I configured CORS on the web api but the error still persists.
API
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:39903");
});
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebAPI", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPI v1"));
}
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Angular Service
import { Inject, Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IEmprestimo } from '../Model/emprestimo.interface';
import { IEmprestimoResult } from '../Model/emprestimoresult.interface';
import { fromEvent, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { strict } from 'assert';
#Injectable({
providedIn: 'root'
})
export class EmprestimoService {
constructor(private http: HttpClient) { }
postEmprestimo(pEmprestimo: IEmprestimo): Observable<IEmprestimoResult>
{
const headers = { 'Content-Type': 'application/json', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Connection':'keep-alive' };
const body = {
"parcelas": pEmprestimo.parcelas,
"valorParcelas": pEmprestimo.valorParcelas,
"valorEmprestimo": ""
};
var result = this.http.post<IEmprestimoResult>('http://localhost:15741/api/Home/CalculaEmprestimo', body);
return result;
}
}
Can anyone help me resolve this?
Make sure to include AllowAnyHeader() and AllowAnyMethod() before WithOrigins("...")
If you're using Angular and you didn't change default port then you should rather use WithOrigins("http://localhost:4200");
Sometimes you need to enable Cors on some browsers, try to run on multiple browsers and check if the problem persists.

Authentication in ASP.Net Core 5

I created asp.net core with React template in VS 2019, i need to authorize a controller method so I first registered my app on Azure AD and than i used this Startup.cs configurations:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddControllersWithViews().AddMicrosoftIdentityUI();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
In the controller I used AuthorizeForScopes and ITokenAcquisition as follows
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ITokenAcquisition tokenAcquisition;
public WeatherForecastController(ITokenAcquisition tokenAcquisition)
{
this.tokenAcquisition = tokenAcquisition;
}
[AuthorizeForScopes(Scopes = new[] { "https://tenantname.sharepoint.com/AllSites.FullControl" })]
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "https://tenantname.sharepoint.com/AllSites.FullControl" });
......
......
}
}
but when i try to fetch the data i have a CORS error
Can you help me
Regarding the issue, please refer to the following steps
Register client application and server application
Use React project template with ASP.NET Core
Client application
a. Install msal
npm install msal
b. Define MsalAuthProvider.
import React, { Component } from 'react';
import { UserAgentApplication } from 'msal';
const msalConfig = {
authority: 'https://login.microsoftonline.com/common',
clientId: '232a1406-b27b-4667-b8c2-3a865c42b79c',
redirectUri: document.getElementById('root').baseURI
};
export const msalAuth = new UserAgentApplication({
auth: msalConfig
});
export function withAuth(HocComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: false,
user: {},
renewIframe: false,
hasError: false,
errorMessage: null
};
}
async componentWillMount() {
msalAuth.handleRedirectCallback(() => {
let userAccount = msalAuth.getAccount();
this.setState({
isAuthenticated: true,
user: userAccount
});
}, (authErr, accountState) => { // on fail
console.log(authErr);
this.setState({
hasError: true,
errorMessage: authErr.errorMessage
});
});
if (msalAuth.isCallback(window.location.hash)) {
this.setState({
auth: {
renewIframe: true
}
});
return;
}
let userAccount = msalAuth.getAccount();
if (!userAccount) {
msalAuth.loginRedirect({});
return;
} else {
this.setState({
isAuthenticated: true,
user: userAccount
});
}
}
onSignIn() {
msalAuth.loginRedirect({});
}
onSignOut() {
msalAuth.logout();
}
render() {
if (this.state.renewIframe) {
return <div>hidden renew iframe - not visible</div>;
}
if (this.state.isAuthenticated) {
return <HocComponent auth={this.state} onSignIn={() => this.onSignIn()} onSignOut={() => this.onSignOut()} {...this.props} />;
}
if (this.state.hasError) {
return <div>{this.state.errorMessage}</div>;
}
return <div>Login in progress...</div>;
}
};
}
c. Update App.js
import { withAuth } from './msal/MsalAuthProvider';
import './custom.css'
class RootApp extends Component {
static displayName ="Azure AD application";
render () {
return (
<Layout>
...
</Layout>
);
}
}
//enable auth when we access the page
const App = withAuth(RootApp)
export default App;
call the API
import { msalAuth } from '../msal/MsalAuthProvider'
async componentDidMount() {
// get token and call the api
try {
const accessTokenRequest = {
scopes: ["api://872ebcec-c24a-4399-835a-201cdaf7d68b/access_as_user"]
}
var authRes
var accessToken = null;
try {
authRes = await msalAuth.acquireTokenSilent(accessTokenRequest);
accessToken=authRes.accessToken
}
catch (error) {
console.log("AquireTokenSilent failure");
authRes = await msalAuth.acquireTokenPopup(accessTokenRequest);
accessToken = authRes.accessToken
}
console.log(accessToken)
this.populateWeatherData(accessToken);
}
catch (err) {
var error = {};
if (typeof (err) === 'string') {
var errParts = err.split('|');
error = errParts.length > 1 ?
{ message: errParts[1], debug: errParts[0] } :
{ message: err };
} else {
error = {
message: err.message,
debug: JSON.stringify(err)
};
}
this.setState({
user: {},
isLoading: false,
error: error
});
}
}
async populateWeatherData(token) {
const response = await fetch('weatherforecast', {
method: 'get',
headers: new Headers({
'Authorization': 'Bearer ' + token
})
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Server code
a. Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddControllersWithViews();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
b. Controller
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ITokenAcquisition tokenAcquisition;
public WeatherForecastController(ITokenAcquisition tokenAcquisition)
{
this.tokenAcquisition = tokenAcquisition;
}
[AuthorizeForScopes(Scopes = new[] { "<your scope>" })]
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "<your scope>" });
......
......
}
}
For more details, please refer to here snd here.

ASP .NET Core 2 API routes to GET method then POST method on first request

I noticed that after I restart my ASP .NET API and send a POST request the API routes to the GET request method and then my POST request method. This only happens on the first POST request after I restart the API. Each POST request after this one routes directly to my POST method without processing the GET request method. Below is the class methods from my API.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
//public class variable
private readonly fujimiContext _context;
//constructor for ValuesController class
public ValuesController(fujimiContext context)
{
_context = context;
}
// GET api/values
// [Authorize(Policy = "RequireReadRole")]
[HttpGet("bins")]
public async Task<IActionResult> GetValues()
{
var values = await _context.FcbinCnfg.ToListAsync();
return Ok(values);
}
// POST api/values
// [Authorize(Policy = "RequireEditRole")]
[HttpPost("sitepost")]
public async Task<IActionResult> Post([FromBody] FcbinCnfg [] fcbincnfg)
{
if (fcbincnfg == null)
{
throw new ArgumentNullException(nameof(fcbincnfg));
}
string WindUser = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string AppName = System.AppDomain.CurrentDomain.FriendlyName;
if (ModelState.IsValid)
{
int i = 0;
foreach (var obj in fcbincnfg){
_context.Update(fcbincnfg[i]);
i++;
}
await _context.SaveChangesAsync();
return StatusCode(201);
}
return BadRequest("this does not work");
}
Startup.cs file
namespace FcBin.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
var connection = #"Server=XXXXX\XXXX;Database=XXXXX;Trusted_Connection=True;";
services.AddDbContext<fujimiContext>(options => options.UseSqlServer(connection));
services.AddCors();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.Configure<IISOptions>(options =>
{
options.AutomaticAuthentication = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// app.UseHsts();
}
app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
app.UseMvc();
}
}
}
Angular Route
import { Routes} from '#angular/router';
import { BinConfigComponent } from './BinConfig/BinConfig.component';
import { BinResolver } from './_resolver/bin.resolver';
export const appRoutes: Routes = [
{path: 'binconfig'
, component: BinConfigComponent
, resolve: {binconfig: BinResolver}, runGuardsAndResolvers: 'always'},
{path: '', redirectTo: 'binconfig', pathMatch: 'full', runGuardsAndResolvers: 'always'}
];
So the issue had nothing to do with my routes or the API. The issue was with my save() function in my Angular front end. I approached the function as a sequential problem when in actuality the browser/client approached my function from and efficiency stand point. Below is what I had that the browser would try to optimize
save() {
if (this.dirtyFlag) {
this.dbService.dbsave(this.binconfig).subscribe( () => {
}, error => {
console.log(error);
});
}
if (this.isFoo && this.valid) {
this.dbService.dbsavebin(this.newbin).subscribe( error => {
console.log(error);
});
} else if (!this.valid && this.isFoo) {
this.toastr.warning('Enter a Bin Id');
}
this.router.navigate(['/binconfig']);
}
Here I was having a route resolver reload the page which I triggered after a save. Tacking the route at the end of this save would result in the browser trying to optimize the POST / GET methods in the save() function and in the route resolver. I solved the issue by instead using the arrow function to execute the router navigation after a successful save.
save() {
if (this.dirtyFlag) {
this.dbService.dbsave(this.binconfig).subscribe( () => {
}, error => {
console.log(error);
}, () => {
this.router.navigate(['/binconfig']);
this.toastr.success('Changes Saved');
this.dirtyFlag = false;
});
}
if (this.isFoo && this.valid) {
this.dbService.dbsavebin(this.newbin).subscribe( () => {
this.router.navigate(['/binconfig']);
this.toastr.success('New Bin Added');
this.isFoo = false;
}, error => {
console.log(error);
});
} else if (!this.valid && this.isFoo) {
this.toastr.warning('Enter a Bin Id');
}
}

signalr with .net core 2.0

I'm trying to use SignalR with core 2.0. If I set transport to LongPolling it works, but I get warnings about timeouts. I'm trying to use it with WebSockets, but I get
WebSocket is not in the OPEN state
I checked lots of similar questions, in most of them it's due to system restrictions, but I'm using Windows 10 and Chrome v.67.
I think the problem is in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<NotifyHub>("/notify");
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:52170/");
}));
services.AddSignalR();
}
Front(TypeScript):
ngOnInit() {
let builder = new HubConnectionBuilder();
this.hubConnection = builder.withUrl("http://localhost:52170/notify", HttpTransportType.WebSockets).build();//'http://localhost:1874/notify').build();
this.hubConnection.serverTimeoutInMilliseconds = 5000;
this.hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log(err)); //'Error while establishing connection :('));
console.log(this.hubConnection);
this.hubConnection.onclose = e => {
this.hubConnection.start();
}
}
I get these two messages from browser:
Information: WebSocket connected to
ws://localhost:52170/notify?id=SJ4cb49oWZtL6lIt4cqhKg WebSocket is not
in the OPEN state
#Jamil, best on your code i have created on project and it is working. Can you provide more details or compare with below code. Let me also know if there is any different.
-- In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//DKS: Makes the SignalR services available to the dependency injection system.
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("http://localhost:53249")
.AllowCredentials();
}));
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
--- home.component
import { Component, OnInit } from '#angular/core';
import { HubConnection } from '#aspnet/signalr';
import * as signalR from '#aspnet/signalr';
#Component({
selector: 'app-home-component',
templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
private _hubConnection: HubConnection | undefined;
public async: any;
message = '';
messages: string[] = [];
constructor() {
}
public sendMessage(): void {
const data = `Sent by you: ${this.message}`;
if (this._hubConnection) {
this._hubConnection.invoke('SendAngular', data);
}
this.messages.push(data);
}
ngOnInit() {
let builder = new signalR.HubConnectionBuilder();
this._hubConnection = builder.withUrl("http://localhost:53249/chatHub", signalR.HttpTransportType.WebSockets).build();//'http://localhost:1874/notify').build();
this._hubConnection.serverTimeoutInMilliseconds = 9999999999999;
this._hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log(err)); //'Error while establishing connection :('));
console.log(this._hubConnection);
this._hubConnection.on('Send', (data: any) => {
const received = `Received by Friend: ${data}`;
this.messages.push(received);
});
//this._hubConnection.onclose = e => {
// this._hubConnection.start();
//}
}
}
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SignalRExample.Hubs
{
public class ChatHub : Hub
{
public Task SendAngular(string data)
{
return Clients.All.SendAsync("Send", data);
}
}
}
comment this line "this.hubConnection.serverTimeoutInMilliseconds = 5000;" and check again.
serverTimeoutInMilliseconds is too small.

Categories