Reputation: 927
I have made a simple angular 5 spa with a .net core 2 backend api. I am trying to send a POST request to register my user but for some reason it fails to inject my payload properties in the post call resulting in the auth controller which is unable to parse the dto object from a null instance. I expect that the error is in my angular code but I checked the official documentation and cannot see what I am doing wrong. Below I will describe my code:
PS: As there are so many parts for such a simple HTTP call I also included the project here: download the source code
My register component calls the 'authService.register' method which does the call:
import { AuthService } from './../_services/auth.service';
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
model: any = [];
@Input() valuesFromHome: any;
@Output() cancelRegister = new EventEmitter();
constructor(private authService: AuthService) { }
ngOnInit() {
}
register() {
console.log(this.model);
this.authService.register(this.model).subscribe(() => {
console.log('registration successful');
}, error => {
console.log(error);
});
}
cancel() {
this.cancelRegister.emit(false);
console.log('cancelled');
}
}
Here is the auth.service
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map';
@Injectable()
export class AuthService {
baseUrl = 'http://localhost:5000/api/auth';
userToken: any;
constructor(private http: Http) { }
login(model: any) {
return this.http.post(this.baseUrl + '/login', model, this.requestOptions()).map((response: Response) => {
const user = response.json();
if (user) {
localStorage.setItem('token', user.tokenString);
this.userToken = user.tokenString;
}
});
}
register(model: any) {
console.log('model before the call', model);
return this.http.post(this.baseUrl + '/register', model, this.requestOptions());
}
private requestOptions() {
const headers = new Headers({'Content-type': 'application/json'});
return new RequestOptions({headers: headers});
}
}
When making a post the "model before the call" log will execute just fine with the correct model:
When we check the request however it is empty:
Next when I debug my auth controller gets hit and when using postman the data is inserted, but via my SPA it gives an error 'registerDto cannot be null'.
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using DatingApp.API.Data;
using DatingApp.API.Dtos;
using DatingApp.API.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace DatingApp.API.Controllers
{
[AllowAnonymous]
[Route("api/[controller]")]
public class AuthController : Controller
{
private readonly IAuthRepository _repository;
private readonly IConfiguration _config;
public AuthController(IAuthRepository repository, IConfiguration config)
{
this._config = config;
this._repository = repository;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] userForRegisterDto registerDto)
{
registerDto.Username = registerDto.Username.ToLower();
if (await _repository.UserExists(registerDto.Username))
{
ModelState.AddModelError("Username", "USername already exists");
}
if (!ModelState.IsValid)
return BadRequest(ModelState);
var userToCreate = new User
{
UserName = registerDto.Username
};
var createUser = await _repository.Register(userToCreate, registerDto.Password);
return StatusCode(201);
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] UserForLoginDto userForLoginDto)
{
var userFromRepo = await _repository.Login(userForLoginDto.UserName.ToLower(), userForLoginDto.Password);
if (userFromRepo == null)
{
return Unauthorized();
}
// generate token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_config.GetSection("AppSettings:Token").Value);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.NameIdentifier, userFromRepo.Id.ToString()),
new Claim(ClaimTypes.Name, userFromRepo.UserName)
}),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha512)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new { tokenString });
}
}
}
The userForRegisterDto
using System.ComponentModel.DataAnnotations;
namespace DatingApp.API.Dtos
{
public class userForRegisterDto
{
[Required]
public string Username { get; set; }
[Required]
[StringLength(8, MinimumLength = 4, ErrorMessage = "You must specify a password between 4 and 8 characters")]
public string Password { get; set; }
}
}
I checked the Angular documentation and the parameters all match up. The docs can be found here: https://angular.io/guide/http
The model:
namespace DatingApp.API.Models
{
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
}
}
Upvotes: 0
Views: 1426
Reputation: 167
Try
replacing
model:any = []
, withmodel:any = {}
;
You're creating an array not an object.
Upvotes: 3