public async Task <UserViewModel> Handle(CreateUserCommand request, CancellationToken cancellationToken) { // Validate the username is not in use var existingUserByUserName = await _userManager.FindByNameAsync(request.User.Username); if (existingUserByUserName != null) { throw new ConduitApiException($"Username {request.User.Username} is already in use", HttpStatusCode.BadRequest); } // Validate the email is not in use var existingUserByEmail = await _userManager.FindByEmailAsync(request.User.Email); if (existingUserByEmail != null) { throw new ConduitApiException($"Email {request.User.Email} is already in use", HttpStatusCode.BadRequest); } // Instantiate and attempt to create the user var newUser = new ConduitUser { UserName = request.User.Username, Email = request.User.Email, }; var createUserResult = await _userManager.CreateAsync(newUser, request.User.Password); // Instantiate the creation exception and errors, if necessary if (!createUserResult.Succeeded) { var exception = new ConduitApiException($"Could not create user with [{request.User.Username}]", HttpStatusCode.BadRequest); foreach (var error in createUserResult.Errors) { var conduitError = new ConduitApiError(error.Code, error.Description); exception.ApiErrors.Add(conduitError); } throw exception; } // Generate the token and map the user var token = _tokenService.CreateToken(newUser); var userViewModel = new UserViewModel { User = _mapper.Map <UserDto>(newUser) }; userViewModel.User.Token = token; await _context.AddActivityAndSaveChangesAsync(ActivityType.UserCreated, TransactionType.ConduitUser, newUser.Id, cancellationToken); _logger.LogInformation($"{request.User.Username}] created successfully"); return(userViewModel); }
/// <inheritdoc /> /// <summary> /// Overrides the default model state validation from ASP.NET Core, passing any validation failures on request /// to API exceptions thrown here so they may be handled manually during the request pipeline. /// </summary> /// <param name="context">HTTP context passed from the web layer</param> /// <exception cref="T:Conduit.Core.Exceptions">API exception handled by the pipeline</exception> public override void OnActionExecuting(ActionExecutingContext context) { var modelState = context.ModelState; if (!modelState.IsValid) { // Retrieve all model state errors var modelErrors = modelState.Keys.SelectMany(key => modelState[key].Errors); // Build a list of ConduitApiErrors to return to the request pipeline var conduitApiErrors = modelErrors.Select(modelError => new ConduitApiError(modelError.ErrorMessage)).ToList(); // Instantiate the exception var conduitApiException = new ConduitApiException( $"Invalid model state during request to [{context.Controller.GetType().Name}]. Trace ID: [{context.HttpContext.TraceIdentifier}]", HttpStatusCode.UnsupportedMediaType, conduitApiErrors); // Throw the API exception to be caught during the pipeline throw conduitApiException; } base.OnActionExecuting(context); }