Reputation: 101
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
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.
and angular
Upvotes: 0
Views: 138
Reputation: 21
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
Upvotes: 1