I am making an Aspnetcore project for a client but facing a problem while getting the user/visitor registered and getting the current logged in visitor.
I have made the modal - Visitor.cs below:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
namespace API.Entities
{
public class Visitor : IdentityUser
{
[Required, MaxLength(200)]
public string Name { get; set; }
[NotMapped]
public IFormFile ProfilePhoto { get; set; }
public string Token { get; set; }
}
}
similaly my loginDTO and registerDTO are as follows-
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace API.DTOs
{
public class RegisterDTO
{
[Required, MaxLength(200)]
public string Name { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
[NotMapped]
public IFormFile ProfilePhoto { get; set; }
public string Password { get; set; }
}
}
loginDTO -
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace API.DTOs
{
public class LoginDTO
{
[Required, MaxLength(200)]
public string UserName { get; set; }
public string Password { get; set; }
}
}
finally i am returning the new VisitorDTO to the client on the successful log in of the user -
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace API.DTOs
{
public class VisitorDTO
{
public string Name { get; set; }
public string Email { get; set; }
public string Token { get; set; }
public string ProfilePhoto { get; set; }
}
}
In order to register the user I am creating the new instance of the Visitor which is effectively taking its data from the registerDTO modal. It is indeed working fine as so do the loginUser() - method, but after logged in I want to get a currentUser which will return the VisitorDTO to the client's browser.
On testing the functionality of the post and get methods for register and login respectively on swagger , they both are working fine as expected. But the currentUser (httpGet) is throwing a 500 error as
"title": "Value cannot be null. (Parameter 'userName')",
"status": 500,
"detail": " at Microsoft.AspNetCore.Identity.UserManager`1.FindByNameAsync(String userName)\n at API.Controllers.VisitorController.GetCurrentVisitor() in /Users/himanshu/SnakeLadder/API/Controllers/VisitorController.cs:line 74\n at lambda_method258(Closure , Object )\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\n--- End of stack trace from previous location ---\n at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\n at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\n at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)\n at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)\n at API.Middleware.customMiddleware.InvokeAsync(HttpContext context) in /Users/himanshu/SnakeLadder/API/Middleware/customMiddleware.cs:line 28"
}
and here is my VisitorController.cs class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs;
using API.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace API.Controllers
{
public class VisitorController : BaseAPIController
{
private readonly UserManager<Visitor> _userManager;
private readonly GameContext _gameContext;
public VisitorController(GameContext gameContext, UserManager<Visitor> userManager)
{
_userManager = userManager;
_gameContext = gameContext;
}
//register visitor
[HttpPost("register")]
public async Task<IActionResult> RegisterUser([FromForm]RegisterDTO registerDTO){
var photoUrl = await SaveProfile(registerDTO.ProfilePhoto);
var registeredVisitor = new Visitor {
Name = registerDTO.Name,
UserName = registerDTO.UserName,
ProfilePhoto = registerDTO.ProfilePhoto,
Email = registerDTO.Email,
Token = ""
};
var result = await _userManager.CreateAsync(registeredVisitor, registerDTO.Password);
if (!result.Succeeded) {
foreach(var Error in result.Errors)
{
ModelState.AddModelError(Error.Code, Error.Description);
}
return ValidationProblem();
}
await _userManager.AddToRoleAsync(registeredVisitor, "Member");
return Ok($"User {registeredVisitor.Name} is successfully registered to the server. Thank You : )");
}
[HttpPost("loginUser")]
public async Task<ActionResult<VisitorDTO>> LoginUser(LoginDTO loginDTO){
var loggedinUser = await _userManager.FindByNameAsync(loginDTO.UserName);
if (loggedinUser == null || !await _userManager.CheckPasswordAsync(loggedinUser, loginDTO.Password)) {
return Unauthorized(new ProblemDetails{
Title = "Unauthorized",
Status = 401,
Detail = "Your Username or Password is incorrect! Please try again..."
});
}
return new VisitorDTO{
Email = loggedinUser.Email,
Name = loggedinUser.Name,
Token = "",
ProfilePhoto = await SaveProfile(loggedinUser.ProfilePhoto),
};
}
[HttpGet("currentVisitor")]
public async Task<ActionResult<VisitorDTO>> GetCurrentVisitor() {
var visitorName = await _userManager.FindByNameAsync(User.Identity.Name);
Console.WriteLine(visitorName);
if(visitorName == null) return BadRequest(new ProblemDetails{Title = "Username cannot be found!"});
return new VisitorDTO{
Email = visitorName.Email,
ProfilePhoto = await SaveProfile(visitorName.ProfilePhoto),
Token = visitorName.Token,
Name = visitorName.Name,
};
/*var uploadedFile = await SaveProfile(visitor.ProfilePhoto);
if(uploadedFile.Length > 0){*/
/*}else{
return BadRequest(new ProblemDetails{
Title= "400 - Bad Request",
Status = 400,
Detail = "Problem While Uploading Profile Picture to the Server."
});*/
}
[NonAction]
public async Task<string> SaveProfile(IFormFile imageFile){
string fileName = null;
if (imageFile != null) {
string uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
//this is encoded file
fileName = "Created" + DateTime.Now.ToString("yymmddhhmmss") + "+" + Guid.NewGuid().ToString() + "_" + Path.GetExtension(imageFile.FileName);
string filePATH = Path.Combine(uploadsFolder, fileName);
using(var fileStream = new FileStream(filePATH, FileMode.Create)){
await imageFile.CopyToAsync(fileStream);
}}
return fileName;
}
private byte[] ReadImage(IFormFile file)
{
using (var target = new MemoryStream())
{
file.CopyTo(target);
return target.ToArray();
}
}
}}
into the HttpGet("CurrentUser")
I am not getting the loggedIN user's data as I wanted.
I have already migrated the data to the database into my storeContext.cs class