Why don't values persist on Postback? - c#

My home page has a list of product options each giving the user a different account level on sign up. Each option presents a "Sign Up" button which takes the user to the /Identity/Account/Register page to sign up.
I need to communicate to the Register page which option the user selected.
I can't use Sessions because that's apparently been taken away
I can't use Cookies because that's apparently been taken away
ViewData values don't persist when I submit the form
Querystring values don't persist when I submit the form
Global variable values don't persist when I submit the form
I can't set properties of the viewmodel when the page is loaded initially (NullReferenceException)
When the Register page loads, the value is there, but when I submit the form, it disappears.
I'm at a loss. By what mechanism am I meant to get this required information across?
For the most part my code is pretty much just standard boilerplate stuff:
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager ?? throw new ArgumentNullException(nameof(signInManager));
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
[BindProperty]
public int AccountLevel { get; set; }
public string ReturnUrl { get; set; }
public void OnGet(string returnUrl = null, int acclevel = 1)
{
AccountLevel = acclevel;
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null, int acclevel = 0)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
if (acclevel == 0) throw new ArgumentException(nameof(acclevel));
Input.LicenseCount = acclevel * 10;
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, Name = Input.FirstName, Surname = Input.Surname, PhoneNumber = Input.PhoneNumber, SaIdNumber = Input.IdNumber, LicensesCount = Input.LicenseCount };
var result = await _userManager.CreateAsync(user, Input.Password);
await _userManager.AddToRoleAsync(user, nameof(SystemRoles.AppUser));
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
//await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
Here's the Page (this is using Pages with the PageModel for some reason, rather than Views and Controllers - it scaffolded this way when I added Identity).
#page
#model RegisterModel
#{
ViewData["Title"] = "Register";
}
<h1>#ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="#Model.ReturnUrl" method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.FirstName"></label>
<input asp-for="Input.FirstName" class="form-control" />
<span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Surname"></label>
<input asp-for="Input.Surname" class="form-control" />
<span asp-validation-for="Input.Surname" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.PhoneNumber"></label>
<input asp-for="Input.PhoneNumber" class="form-control" />
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.IdNumber"></label>
<input asp-for="Input.IdNumber" class="form-control" />
<span asp-validation-for="Input.IdNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}

Note: I don't work with Razor Pages, so someone with knowledge in that technology might have a better answer.
You can use a hidden field in the form to keep the value on POST-back:
<form asp-route-returnUrl="#Model.ReturnUrl" method="post">
#Html.HiddenFor(model => model.AccountLevel)
#*OR, TagHelper way*#
<input asp-for="AccountLevel" type="hidden"/>
<h4>Create a new account.</h4>
....
</form>
This is also fairly easy to do with sessions after you enable them:
public void OnGet(string returnUrl = null, int acclevel = 1)
{
HttpContext.Session.SetInt32("AccountLevel", acclevel);
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
int accountLevel = HttpContext.Session.GetInt32("AccountLevel");
...
}

The only data that exists after a request is what's sent along with that request. Even in the case of sessions, the crucial session identifier must be transmitted by the client to the server in the request in order to restore that session.
Any data you retrieved from the database must either be posted or queried out again. Additionally, no data should be posted that the user should not be explicitly allowed to change. As a result, most data should to be queried again.
As far as which option the user selected goes, that of course should be posted. However, the actual details of the selected plan and the other plan options should be obtained from querying that database again. That will be necessary, for example, if you need to redisplay the form following the post, due to a validation error.

Related

How to show a temporally Success Message without using query parameters?

I want to show a success message in a view SingIn.cshtml only one time after user registration. In order to do it, i was thinking into sending a boolean parameter to the SingIn Action method so then the View SingIn.cshtml will get it, and then choose if it should show it or not, but since the parameter will appear in the query string (app.com/Account/SignIn?parameter=true), the user can refresh the page and see it again and again or maybe he can type the url and saw it again.So How can i complete my approach without showing the parameter on a query string (app.com/Account/SignIn) ?
This is my Controller:
public class AccountController : Controller
{
private readonly SignInManager<Client> _signInManager;
private readonly UserManager<Client> _userManager;
public AccountController(UserManager<Client> userManager, SignInManager<Client> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[HttpGet]
public IActionResult SignUp()
{
return View(new SignUpViewModel());
}
[HttpPost]
public async Task<IActionResult> SignUp(SignUpViewModel viewModel)
{
if (ModelState.IsValid)
{
var client = new Client
{
UserName = viewModel.Id, FullName = viewModel.FullName, BirthDate = viewModel.BirthDate.Value,
Email = viewModel.Email
};
var result = await _userManager.CreateAsync(client, viewModel.Password);
if (result.Succeeded)
return RedirectToAction("SignIn", new {DidHeJustSignUp = true});
}
return View(viewModel);
}
[HttpGet]
public IActionResult SignIn(bool didHeJustSignUp)
{
var model = new SignInViewModel {DidHeJustSignUp = didHeJustSignUp};
return View(model);
}
}
This is my view:
#model SignInViewModel
#{
ViewBag.Title = "Sign In";
}
<form asp-controller="Account" asp-action="SignIn" method="post">
#{
if (Model.DidHeJustSignUp)
{
<div class="alert alert-success text-center">
You have been registred Successfully, please Sign In
</div>
}
}
<div class="form-group">
<label asp-for="Id"></label>
<input type="text" class="form-control" asp-for="Id">
<span class="text-danger" asp-validation-for="Id"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input type="password" class="form-control" asp-for="Password"/>
<span class="text-danger" asp-validation-for="Password"></span>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" asp-for="ShouldIRememberYou">
<label class="form-check-label" asp-for="ShouldIRememberYou">Remember me</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
You can use TempData here, anything you put into TempData is discarded after the next request completes.
if (result.Succeeded)
{
TempData["DidHeJustSignUp"] = true;
return RedirectToAction("SignIn");
}
if (TempData["DidHeJustSignUp"] != null)
{
<div class="alert alert-success text-center">
You have been registred Successfully, please Sign In
</div>
}

Blazor Role Management Add Role trough UI (Crud)

I'm pretty new to blazor and have gotten myself in some doubt on adding roles to the database.
I have implemented to Identity role management and have a working system.
But now i want to add new roles trough the GUI instead of editing the database.
I have a razor page called RolesOverview.razor
On this page i have a input field and a button.
When i click this button i want to add the text to the roles manager and save it to the database.
This is my razor component
#page "/admin/roles"
#using Microsoft.AspNetCore.Identity
#inject RoleManager<IdentityRole> roleManager
<div class="jumbotron">
<!-- Roles Overview Group Box -->
<div class="row mb-5">
<div class="col-12">
<h1 class="display-6">Roles Options</h1>
<hr class="my-4" />
<div class="row" style="background-color:white; margin-bottom:10px; margin-top:10px;">
<div class="col-12">
<div class="card w-100 mb-3" style="min-width:100%;">
<div class="card-body">
<h5 class="card-title">Roles</h5>
<p class="card-text">
<div class="row">
<div class="col-1">
Role Name:
</div>
<div class="col-10">
<input type="text" style="min-width:100%;" placeholder="Role Type" />
</div>
<div class="col-1">
Add Role
</div>
</div>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Not Getting Saved...
#code {
private string CurrentValue { get; set; }
private async void AddRole()
{
if (CurrentValue != string.Empty)
{
if (!await roleManager.RoleExistsAsync(CurrentValue))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = CurrentValue
});
}
}
}
}
I have no clue on what todo next.
I's it posible todo it with razor component or do i need to do it trough razor page?
Example would be perfect.
Regards Me!
Answer :
<div class="col-10">
<input value="#CurrentValue" #onchange="#((ChangeEventArgs __e) => CurrentValue =__e.Value.ToString())" />
#*<input type="text" style="min-width:100%;" placeholder="Role Type" />*#
</div>
<div class="col-1">
<a #onclick="AddRole" class="btn btn-primary" style="min-width:90px;">Add Role</a>
</div>
#code {
private string CurrentValue { get; set; }
private async void AddRole()
{
if (CurrentValue != string.Empty)
{
if (!await roleManager.RoleExistsAsync(CurrentValue))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = CurrentValue
});
}
}
}
}
You can use RoleManager to create a new role by using the CreateAsync method:
if (!await roleMgr.RoleExistsAsync("RoleName"))
{
await roleManager.CreateAsync(new IdentityRole
{
Name = "RoleName"
});
}

Using IsPost in asp.net core razor page to display text after form submission

I'm trying to use the IsPost method in my asp.net core web page, but it says it does not exist in the current context. Since I'm not using MVC, (other than the fact that I have a models folder), is it possible for me to use IsPost in my razor page? Basically I am trying to display confirmation text on the same page after the user hits the submit button, so if anyone has a better way of doing this, please suggest so. Thanks
FreeConsultation.cshtml
#page
#model GuptaAccounting.Pages.FreeConsultationModel
#{
ViewData["Title"] = "FreeConsultation";
}
<head>
<script src="~/js/site.js"></script>
</head>
<div class="container" style="padding:30px;">
<br />
<h1 class="text-info">Get a FREE Consultation</h1>
<br />
#if (IsPost)
{
<p>Consultation requested. I will get back to you as soon as possible</p>
}
else
{
<form method="post">
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<!-- More of the form here -->
<div class="form-group row">
<div class="col-3 offset-3">
<input type="submit" value="Submit" onclick="return Validate()" class="btn btn-primary form-control" />
</div>
</div>
</form>
}
FreeConsultation.cshtml.cs
public class FreeConsultationModel : PageModel
{
private readonly ApplicationDbContext _db;
public FreeConsultationModel(ApplicationDbContext db)
{
_db = db;
}
[BindProperty]
public Client Client { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
Client.IsConsultationClient = true;
await _db.Client.AddAsync(Client);
await _db.SaveChangesAsync();
return RedirectToPage("Index");
}
else
{
return Page();
}
}
}
I don't think razor pages have an IsPost method but you could add an IsPost property to your FreeConsultationModel and set it to true in the OnPost() method.

Identity Core 2 issue with registration

I am trying to use a model to create a user using asp.net core 2 sql server and identity core. But I am having an issue registering the user the following is my code and the errors it generates. I hope someone can help me.
This is not just a null issue its an issue with the posting of the form not getting the correct model so the persons interpetation is incorrect.
Model:
public class Register
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
To Which I have a register action marked with the post command
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(AppUser model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
var user = new AppUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
// "Please confirm your account by clicking this link: link");
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is my html for the form
#model solitude.models.Register
#{
ViewData["Title"] = "Register";
Layout = "~/Views/Shared/_LoginAdminLte.cshtml";
}
<body class="hold-transition register-page">
<div class="register-box">
<div class="register-logo">
<b>Register</b>
</div>
<div class="register-box-body">
<p class="login-box-msg">Register a new membership</p>
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
<div class="form-group has-feedback">
<input type="text" class="form-control" placeholder="Full name">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input asp-for="Email" class="form-control" placeholder="Email">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input asp-for="Password" class="form-control" />
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input asp-for="ConfirmPassword" class="form-control" />
<span class="glyphicon glyphicon-log-in form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox"> I agree to the terms
</label>
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Register</button>
</div>
<!-- /.col -->
</div>
</form>
But I am having the following error problem
System.NullReferenceException: Object reference not set to an instance
of an object. at
solitude.admin.core.Controllers.AccountController.d__6.MoveNext()
in
C:\Projects\solitudeec2core\solitude.admin.core\solitude.admin.core\Controllers\AccountController.cs:line
88
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
When I look at line 88 it shows this but I do not see why I am having this problem
var result = await _userManager.CreateAsync(user, model.Password);
My Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=----SEVER NAME HIDDEN---;Database=solitude;Trusted_Connection=True;";
services.AddDbContext<SolitudeDBContext>(options => options.UseSqlServer(connection));
services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<SolitudeDBContext>()
.AddDefaultTokenProviders();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Does any body have an idea what could be wrong.
The error is on this line:
public async Task<IActionResult> Register(AppUser model, string returnUrl = null)
You should provide Register type as an model input parameter, and not AppUser:
public async Task<IActionResult> Register(Register model, string returnUrl = null)

IdentityCore Login or Register doesn't redirect back to home/index

i'm having problem with IdentityCore.
When i press the login button on the login form i do not get redirected to the index view, same with the register button.
This is how debug goes
I fill in the form correctly
I press the login button
The Login Action is called
Modelstate is valid and result succeeded
"User logged in" gets logged
RedirectToLocal(null) gets returned
Dependency injection calls
The Index action in home controller is called
Dishes gets loaded correctly
it returns View(dishes)
it stays in the login view instead of going to home/index
Login Form:
<form asp-route-returnurl="#ViewData["ReturnUrl"]" method="post">
<h4>Use a local account to log in.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
#Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
<div class="form-group">
<p>
<a asp-action="ForgotPassword">Forgot your password?</a>
</p>
<p>
<a asp-action="Register" asp-route-returnurl="#ViewData["ReturnUrl"]">Register as a new user?</a>
</p>
</div>
</form>
Login Action:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
HomeController:
public class HomeController : Controller
{
private ApplicationDbContext _context;
public HomeController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Index()
{
var dishes = _context.Dishes
.Include(d => d.IngredientDish)
.ThenInclude(id => id.Ingredient)
.Include(d => d.Category)
.ToList();
return View(dishes);
}
}
If you need more codesnippets or information do let me know

Categories

Resources