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); }
/// <summary> /// Handles any exception thrown during the pipeline process and in the application layer. Note that model state /// validation failures made in the web layer are handled by the ASP.NET Core model state validation failure filter. /// </summary> /// <param name="context">HTTP context from the request pipeline</param> /// <param name="exception">Exceptions thrown during pipeline processing</param> /// <returns>Writes the API response to the context to be returned in the web layer</returns> private static async Task HandleExceptionAsync(HttpContext context, Exception exception) { ErrorDto errors; ICollection <object> errorList = new List <object>(); /* * Handle exceptions based on type, while defaulting to generic internal server error for unexpected exceptions. * Each case handles binding the API response message, API response status code, the HTTP response status code, * and any errors incurred in the application layer. Validation failures returned from Fluent Validation will * be added to the API response if there are any instances. */ switch (exception) { case ConduitApiException conduitApiException: errors = new ErrorDto(conduitApiException.Message); context.Response.StatusCode = (int)conduitApiException.StatusCode; if (conduitApiException.ApiErrors.Any()) { errors.Details = conduitApiException.ApiErrors; } break; case ValidationException validationException: context.Response.StatusCode = (int)HttpStatusCode.UnsupportedMediaType; foreach (var validationFailure in validationException.Errors) { var conduitValidationError = new ConduitApiError(validationFailure.ErrorMessage, validationFailure.PropertyName); errorList.Add(conduitValidationError); } errors = new ErrorDto(ConduitErrorMessages.ValidationError, errorList); break; default: errors = new ErrorDto(ConduitErrorMessages.InternalServerError); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; break; } // Instantiate the response context.Response.ContentType = "application/json"; var errorResponse = new ErrorViewModel(errors); // Serialize the response and write out to the context buffer to return var result = JsonConvert.SerializeObject(errorResponse, ConduitConstants.ConduitJsonSerializerSettings); await context.Response.WriteAsync(result); }