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>
}
Related
I'm having trouble to update the image of an author in my application. When I add the input field for files in my Razor pages the code doesn't jump to the OnPost method in the code behind. Without the file input field it does.
Below my code.
Razor page code:
#page
#model PersonalLibrary.Razor.Pages.Authors.EditModel
#{
}
<div class="container">
<h1>Edit</h1>
<h4>Author</h4>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Author.Id" />
<div class="form-group">
<img src="#Html.DisplayFor(model => model.Author.Image)" />
<div class="file-field input-field">
<div class="btn">
<span>Browse</span>
<input asp-for="Author.Image" type="file" />
</div>
<div class="file-path-wrapper">
<input asp-for="Author.Image" class="file-path validate" type="text" placeholder="Upload file" />
</div>
</div>
</div>
<div class="form-group">
<label asp-for="Author.Name" class="control-label"></label>
<input asp-for="Author.Name" class="form-control" />
<span asp-validation-for="Author.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Author.Country" class="control-label"></label>
<input asp-for="Author.Country" class="form-control" />
<span asp-validation-for="Author.Country" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Author.DateOfBirth" class="control-label"></label>
<input asp-for="Author.DateOfBirth" class="datepicker form-control" />
<span asp-validation-for="Author.DateOfBirth" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
</form>
</div>
OnPost in the code behind:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
IFormFile image = (IFormFile)Author.Image;
var imageUri = $"{baseUri}/authors/{Author.Id}/images";
await WebApiClient.PutCallApi<Author, IFormFile>(imageUri, image);
var uri = $"{baseUri}/authors/{Author.Id}";
await WebApiClient.PutCallApi<Author, Author>(uri, Author);
return RedirectToPage("./Index");
}
Api controller methods:
[HttpPut("{id}")]
public async Task<IActionResult> PutAsync(AuthorRequestDto authorRequestDto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var authorResponseDto = await _authorService.UpdateAsync(authorRequestDto);
return Ok(authorResponseDto);
}
[HttpPost("{id}/image"), HttpPut("{id}/image")]
public async Task<IActionResult> Image([FromRoute] Guid id, IFormFile image)
{
var author = await _authorService.AddOrUpdateImageAsync(id, image);
if (author == null)
{
return NotFound($"Author with ID {id} not found");
}
return Ok(author);
}
Any help welcome!
UPDATE
In the Edit.cshtml.cs file I've added a new BindProperty for the image.
namespace PersonalLibrary.Razor.Pages.Authors
{
public class EditModel : PageModel
{
private readonly string baseUri = Constants.ApiBaseUri.BaseUri;
[BindProperty]
public Author Author { get; set; }
[BindProperty]
public IFormFile Image { get; set; }
public async Task<IActionResult> OnGetAsync(Guid id)
{
if (id == null)
{
return NotFound();
}
var uri = $"{baseUri}/authors/{id}";
Author = await WebApiClient.GetApiResult<Author>(uri);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var imageUri = $"{baseUri}/authors/{Author.Id}/image";
await WebApiClient.PutCallApi<IFormFile, IFormFile>(imageUri, Image);
//var uri = $"{baseUri}/authors/{Author.Id}";
//await WebApiClient.PutCallApi<Author, Author>(uri, Author);
return RedirectToPage("./Index");
}
}
}
I had to change the enctype for the form to "multipart/form-data" in the Edit.cshtml file, like shown below.
#page
#model PersonalLibrary.Razor.Pages.Authors.EditModel
#{
}
<div class="container">
<h1>Edit</h1>
<h4>Author</h4>
<form method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Author.Id" />
<div class="form-group">
<img src="#Html.DisplayFor(model => model.Author.Image)" />
<div class="file-field input-field">
<div class="btn">
<span>Browse</span>
<input asp-for="Image" type="file" />
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text"
placeholder="Upload file" />
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
</form>
</div>
So now the Image property is filled with the image I want to upload, but it crashes when accessing the WebApiClient class.
namespace PersonalLibrary.Web.Helpers
{
public class WebApiClient
{
private static JsonMediaTypeFormatter GetJsonFormatter() {
var formatter = new JsonMediaTypeFormatter();
//prevent self-referencing loops when saving Json (Bucket -> BucketItem -> Bucket -> ...)
formatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return formatter;
}
public async static Task<T> GetApiResult<T>(string uri)
{
using (HttpClient httpClient = new HttpClient())
{
string response = await httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(response, GetJsonFormatter().SerializerSettings);
}
}
public static async Task<TOut> PutCallApi<TOut, TIn>(string uri, TIn entity)
{
return await CallApi<TOut, TIn>(uri, entity, HttpMethod.Put);
}
public static async Task<TOut> PostCallApi<TOut, TIn>(string uri, TIn entity)
{
return await CallApi<TOut, TIn>(uri, entity, HttpMethod.Post);
}
public static async Task<TOut> DeleteCallApi<TOut>(string uri)
{
return await CallApi<TOut, object>(uri, null, HttpMethod.Delete);
}
private static async Task<TOut> CallApi<TOut, TIn>(string uri, TIn entity, HttpMethod httpMethod)
{
TOut result = default;
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response;
if (httpMethod == HttpMethod.Post)
{
response = await httpClient.PostAsync(uri, entity, GetJsonFormatter());
}
else if (httpMethod == HttpMethod.Put)
{
response = await httpClient.PutAsync(uri, entity, GetJsonFormatter());
}
else
{
response = await httpClient.DeleteAsync(uri);
}
result = await response.Content.ReadAsAsync<TOut>();
}
return result;
}
}
}
This is the error I'm receiving:
UnsupportedMediaTypeException: No MediaTypeFormatter is available to read an object of type 'IFormFile' from content with media type 'application/problem+json'.
System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
My guess is the problem is I use a JsonMediaTypeFormatter. But I can't figure out what to use to handle files or images.
In ASP.NET Core MVC, I'm facing trouble creating a login panel, I'm using sessions after the user is logged into the account and I'm storing the session values inside the ViewBag. But the ViewBag does not get any value inside of it, it rather gets null value inside it.
Here's the controller
[HttpPost]
public IActionResult Login(userModel model)
{
var findValue = _context.users.Any(o => o.username == model.username);
var findValue2 = _context.users.Any(o => o.password == model.password);
if (findValue && findValue2)
{
HttpContext.Session.SetString("Username", model.username);
}
return View(model);
}
public IActionResult Index()
{
ViewBag.Username = HttpContext.Session.GetString("Username");
return View();
}
And here's the view
Index.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Portal";
}
<h1>Welcome #ViewBag.Username</h1>
Login.cshtml
#model ComplaintManagement.Models.userModel
#{
ViewData["Title"] = "Login";
}
<div class="row mb-3">
<div class="col-lg-4"></div>
<div class="col-lg-4 border login" style="background-color: #d3d1d1;">
<h4 class="mt-3 text-center">
<i class="fa fa-lg fa-user text-secondary"></i><br />
Login
</h4>
<hr />
<form method="post" asp-action="Index" asp-controller="Portal">
<div class="text-danger"></div>
<div class="text-warning">#ViewBag.Name</div>
<div class="form-group">
<label class="mt-4 asp-for=" username"">Username</label>
<input class="form-control" type="text" required="required" asp-for="username" />
<span></span>
</div>
<div class="form-group">
<label class="mt-4" asp-for="password">Password</label>
<input type="password" class="form-control" required="required" asp-for="password" />
<span></span>
</div>
<center>Don't have an account? <a asp-controller="Portal" asp-action="Register">Register here</a>.</center>
<center><button value="login" class="btn btn-primary mt-3 w-25 mb-3 align-content-center">Login</button></center>
</form>
</div>
<div class="col-lg-4"></div>
</div>
Session and state management in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1
Here is a demo How to use Session in ASP.NET Core.
1. Codes of Startup Configurations
AddSession in ConfigureServices, UseSession in Configure.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSession();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseStaticFiles();
....
}
2. Codes of Controller
public class AccountController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public IActionResult Login(userModel model)
{
if (string.IsNullOrEmpty(model.username
) || string.IsNullOrEmpty(model.password))
{
return NotFound();
}
//var user = await _context.users.FirstOrDefaultAsync(x => x.username == model.username && x.password == model.password);
//if (user != null)
if (model.username.Equals("test") && model.password.Equals("123"))
{
HttpContext.Session.SetString("username", model.username);
}
else
ViewBag.error = "Invalid Account";
return View("Index");
}
[HttpGet]
public IActionResult Logout()
{
HttpContext.Session.Remove("username");
return RedirectToAction("Index");
}
}
3. Codes of View
<h3>Login Page</h3>
#ViewBag.error
<form method="post" asp-controller="account" asp-action="login">
<table border="0" cellpadding="2" cellspacing="2">
<tr>
<td>Username</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Login"></td>
</tr>
</table>
</form>
4. Codes of returning View when success
#using Microsoft.AspNetCore.Http;
<h3>Success Page</h3>
Welcome #Context.Session.GetString("username")
<br>
<a asp-controller="account" asp-action="logout">Logout</a>
Test result
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.
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.
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