How can I check in the login whether the email exists in a database - razor page? - asp.net-core

I want the site login form to check if the email is in the system or not, to return an error message.
I can not do this because I am new. I tried to do something I think that's the direction, I'd be happy if you could help me.
public class ConnectionAttribute : ValidationAttribute
{
private readonly OrderContext _context;
private readonly string _connectingMail;
public ConnectionAttribute(string connectingMail)
{
_connectingMail = connectingMail;
}
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
List<Customer> allCustomers = _context.Customer.ToList();
List<string> allMails = new List<string>();
foreach (var item in allCustomers)
{
allMails.Add(item.CustomerMail);
}
var file = value as string;
if (allMails.Contains(_connectingMail))
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
public string GetErrorMessage()
{
return $".";
}
}
I think this code is correct, but I do not know what to call it after clicking connect .
What can you call him?

You could try to use Remote validation.
First, Remote validation in ASP.NET (Core) relies on Unobtrusive AJAX, so you will need to install that first. The easiest way to do this is via LibMan. Please check the wwwroot/lib folder whether you have installed them or not. If doesn't install it, refer the following steps:
Please right click on the lib folder in wwwroot, choose Add » Client-side Library, and then choose jsdelivr as the source, and type in jquery-ajax-unobtrusive.
Second, since your application is an Asp.net Core Razor application, in the PageModel, add Email property with PageRemote attribute and a OnPostCheckEmail method to check whether the email is exist or not:
public class RemoteValidateModel : PageModel
{
[PageRemote(ErrorMessage = "Email Address already exists", AdditionalFields = "__RequestVerificationToken",
HttpMethod = "post",PageHandler = "CheckEmail")]
[BindProperty]
public string Email { get; set; }
public void OnGet()
{
}
//this method is used to check whether the email is exist or not.
public JsonResult OnPostCheckEmail()
{
//query the database and get all existing Emails or directly check whether the email is exist in the database or not.
var existingEmails = new[] { "jane#test.com", "claire#test.com", "dave#test.com" };
var valid = !existingEmails.Contains(Email);
return new JsonResult(valid);
}
}
Code in the Razor Page:
#page
#model RazorSample.Pages.RemoteValidateModel
#{
}
<form method="post">
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span><br>
<input type="submit" />
</form>
#section scripts{
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<partial name="_ValidationScriptsPartial" />
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"></script>
}
Then, the result as below:
More detail information, please check this tutorial:
Remote Validation in Razor Pages
Besides, if you want to use Remote validation in Asp.net Core MVC application, you can check [Remote] attribute.

Related

passing blazor parameters to another page

I have been trying to pass parameters trough another page and this works, however i'm not getting what I desired and it has probably to do with what i pass.
The first thing i pass is a name but includes spaces and special character, the second thing i pass is a web link
how i send it:
<div class="col-sm-4">
<h3>Programming</h3>
#if (programming == null)
{
<p><em>Loading...</em></p>
}
else
{
foreach (var program in programming)
{
#program.Name
<br />
}
}
</div>
where it goes to
#page "/CourseDetails"
#using Portfolio.Models;
#using Portfolio_Frontend.Data;
#using Microsoft.AspNetCore.WebUtilities
#inject NavigationManager NavigationHelper
<h3>CourseDetails</h3>
#if (Name == null)
{
<p><em>Loading...</em></p>
}
else
{
<p>#Name</p>
}
#code {
public string Name { get; set; }
protected override void OnInitialized()
{
var uri = NavigationHelper.ToAbsoluteUri
(NavigationHelper.Uri);
if (QueryHelpers.ParseQuery(uri.Query).
TryGetValue("name", out var name))
{
Name = name.First();
}
}
}
i tried parameters as well and now tried query string gives the same result.
the name it should pass in this particular case is: C# Intermediate: Classes, Interfaces and OOP
What i get is only 'C' I assume because it is not able to translate the #.
is there a way to pass literal strings?
where it goes to: https://localhost:5105/CourseDetails/?name=C#%20Intermediate:%20Classes,%20Interfaces%20and%20OOP
this seems right to me.
Minor correction of URL syntax methodology
You have:
#program.Name
Which has a URL of /CourseDetails/?name=C#
Normally, you would do either
/CourseDetails/C#
/CourseDetails?name=C#
Except, Blazor doesn't explicitly support optional route parameters (/CourseDetails?name=C#)
REF: https://blazor-university.com/routing/optional-route-parameters/#:~:text=Optional%20route%20parameters%20aren%E2%80%99t%20supported%20explicitly%20by%20Blazor,,then%20replace%20all%20references%20to%20currentCount%20with%20CurrentCount.
It looks as though you can keep the optional query parameters and fiddle with the QueryHelpers.ParseQuery() I don't quite buy into that but if you want to keep going that route check out this post by #chris sainty
Link: https://chrissainty.com/working-with-query-strings-in-blazor/
I would much rather create a new model (DTO) that knows exactly how to display the CourseDetails name in a URL encoded fashion for the link, and the display name for the user.
public class ProgramModel
{
private readonly string name;
public ProgramModel(string name)
{
this.name = name;
}
public string DisplayName => name;
public string RelativeUrl => HttpUtility.UrlEncode(name);
}
And when we need to render the links on the 'Courses' page, it would look like this:
#page "/courses"
#using BlazorApp1.Data
<div class="col-sm-4">
<h3>Programming</h3>
#foreach (var program in programming)
{
#program.DisplayName
<br />
}
</div>
#code {
public IEnumerable<ProgramModel> programming { get; set; }
protected override void OnInitialized()
{
programming = new List<ProgramModel>()
{
new ProgramModel("Rust Things"),
new ProgramModel("JavaScript Things"),
new ProgramModel("C# Things")
};
}
}
And finally, when displaying the CourseDetails page, we can simply decode the name from the URL with the same utility that encoded the string in the first place, instead of guessing whether or not it's the apps fault, or the browsers fault that the '#' is not getting encoded properly to '%23'
#page "/CourseDetails/{Name}"
#inject NavigationManager NavigationHelper
#using System.Web
<h3>CourseDetails</h3>
<p>#HttpUtility.UrlDecode(Name)</p>
#code {
[Parameter]
public string Name { get; set; }
}
I recommend letting go of the idea of navigating from page to page, and using components:
<div>
#if (SelectedItem is not null)
{
<MyResultsPage SelectedProgramClass=#SelectedItem />
}
</div>
#code
{
ProgramClass SelectedItem {get; set;}
void SomeWayToSelectMyItem(ProgramClass newSelection){
SelectedItem = newSelection;
StateHasChanged();
}
}
Then in your display page, MyResultsPage.blazor
<div>
<div>#SelectedProgramClass.name</div>
. . .
</div>
#code {
[Parameter]
ProgramClass SelectedProgramClass{get; set;}
}
<MyResultsPage> will not show up in any way on the client, or even be initialized, until you've assigned something to SelectedProgramClass.

Razor Pages - Return Error on Duplicate Name

I'm working on a Razor Pages form that takes in a string to create a new customer in a SQL Server Database. I want to make it work so that if the string that is the customer already exists, a prompt comes up that says "This Customer Already Exists". Just to be safe for data integrity.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
int customerCheck = -1; //No Customer ID is -1
try
{
using (var context = new DataWarehouseContext())
{
customerCheck = context.Customer //Tries to grab a Customer with this name
.Where(a => a.Name == Customer.name)
.Select(b => b.CustomerId)
.FirstOrDefault();
}
}
catch (Exception)
{
}
if(customerCheck == -1)
{
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("/Customer/List");
}
else
{
return Page();
}
}
This is the code I have so far in my backend. What happens is that when a user tries to create a new customer, the backend of the page tries to see if it can grab a customer ID that correlates to this name. If it can, then the value of customerCheck is not -1, therefore some error should get printed out.
I don't know what methods can be used to do this, so any help would be great!
I found a solution, and it wasn't hard to implement. When a duplicate customer was found in the backend, I create a ModelState.AddModelError object and fill it with a key and a description of the error. Next, in the frontend, I put it within an H3 tag to print it out like so:
Backend OnPost() Code
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
int customerCheck = 0; //No Customer ID is 0
try
{
using (var context = new DataWarehouseContext())
{
customerCheck = context.Customer //Tries to grab a Customer with this name
.Where(a => a.Name == Customer.name)
.Select(b => b.CustomerId)
.FirstOrDefault();
}
}
catch (Exception)
{
}
if(customerCheck == 0)
{
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("/Customer/List");
}
else
{
ModelState.AddModelError("DuplicateCustomer", "This Customer Already Exists");
return Page();
}
}
So on the frontend, it gets implemented like this:
<h3 align="center" style="color:yellowgreen">#Html.ValidationMessage("DuplicateCustomer")</h3>
When return Page(); is hit, the page is reloaded and the DuplicateCustomer Error appears.
At first, glad to hear you have found a solution.
Besides, I think you could also use the Remote Validation to check whether the Customer is exist or not. Check the following sample code:
Remote validation in ASP.NET (Core) relies on Unobtrusive AJAX, so you will need to install that first. The easiest way to do this is via LibMan. Right click on the lib folder in wwwroot, choose Add » Client-side Library, and then choose jsdelivr as the source, and type in jquery-ajax-unobtrusive, click the "Install" button to install the package.
In the CreateCustomer.cshtml.cs page, add a Email property and use the PageRemote attribute, then, add a handler method to perform the validation.
public class CreateCustomerModel : PageModel
{
private readonly IRepository _repository;
public CreateCustomerModel(IRepository repository)
{
_repository = repository;
}
[PageRemote(ErrorMessage = "Email Address already exists", AdditionalFields = "__RequestVerificationToken", HttpMethod = "post",PageHandler = "CheckEmail")]
[BindProperty]
public string Email { get; set; }
public void OnGet()
{
}
public IActionResult OnPost()
{
if (ModelState.IsValid)
{
//insert data into database.
}
return Page();
}
#pragma warning disable MVC1001 // Filters cannot be applied to page handler methods.
[ValidateAntiForgeryToken]
#pragma warning restore MVC1001 // Filters cannot be applied to page handler methods.
public JsonResult OnPostCheckEmail()
{
//query database and check whether the email is exist or not.
var existingEmails = _repository.GetCustomers().Select(c => c.Email.ToLower()).ToList();
var valid = !existingEmails.Contains(Email.ToLower());
return new JsonResult(valid);
}
In the CreateCustomer.cshtml razor page, add JQuery reference and add a form to enter the values.
#page
#model RazorSample.Pages.CreateCustomerModel
#{
}
<div class="row">
<div class="col-md-4">
<form method="post" asp-antiforgery="true">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
#* add other fields *#
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section scripts{
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<partial name="_ValidationScriptsPartial" />
<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js"></script>
}
After submit the button, the result as below: if the email is exist, it will show the prompt:
[Note] In the above sample, we are adding the properties in the PageModel (instead of nested model), and use it to validate the field. Because, if using nested object, we might meet the 400 Bad Request result. The 400 error is related to the AntiForgeryToken, if you meet this error, try to ignore validate the AntiForgeryToken or custom add the __RequestVerificationToken token at the body or header, check this link.
More detail information about Remote Validation in Razor Pages, check the following articles:
Remote Validation in Razor Pages
Improved Remote Validation in Razor Pages

Issue dynamically populating select list with server-side Blazor

Trying to populate a select drop down list dynamically & every time I run it, I can step through the code just fine all the way through the #foreach block, but then it seems to get stuck in a continuous loop or something & will completely freeze up everything! Struggling because no error is even occurring.
"/api/Reps/Index" Controller works separately just fine. And I even updated it to just return one rep to see if that was an issue, but didn't make any difference.
*Note: Using MatBlazor to implement Material Design.
My .Razor Page:
#inject HttpClient Http
<EditForm Model="#projectParameters">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<MatSelect Label="Rep" #bind-Value="#projectParameters.Rep">
<MatOption Value="">Select</MatOption>
#foreach (var rep in repList)
{
<MatOption Value="#rep.id.ToString()">#rep.name</MatOption>
}
</MatSelect>
</div>
</EditForm>
#code
{
Project projectParameters = new Project();
private MyRep[] repList { get; set; }
protected override async Task OnInitAsync()
{
repList = await Http.GetJsonAsync<MyRep[]>
("/api/Reps/Index");
}
}
Here is my RepsController.cs code, too:
namespace MyProject.Controllers
{
public class RepsController : Controller
{
MyDataAccessLayer objrep = new MyDataAccessLayer();
[HttpGet]
[Route("api/Reps/Index")]
public IEnumerable<MyRep> Index()
{
return objrep.GetAllReps();
}
}
}
Just add #key attribute that equals to your option value, so code example:
<select>
<option #key="#rep.id" value="#rep.id">#rep.name</option>
</select>

ASP.Net Core Razor Pages: How to return the complex model on post?

I created a new ASP.Net Core 2 (Razor Pages) Project
My model is:
public class FormularioGenerico
{
public FormularioGenerico()
{
}
public string IP { get; set; }
public List<string> items { get; set; } = new List<string>();
}
On the page I put
on the page.cshtml.cs
public class EditarModel : PageModel
{
[BindProperty]
public FormularioGenerico ff { get; set; }
[BindProperty]
public string Message { get; set; }
public void OnGet()
{
this.ff = new FormularioGenerico();
ff.IP = "C# FORM";
ff.items.Add("OK1");
ff.items.Add("OK2");
ff.items.Add("OK3");
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var m = ModelState.IsValid; // true
Debug.WriteLine(this.ff.IP); // is Always returning null
Debug.WriteLine(this.ff.items.Count); // is Always returning null
}
}
on the page.cshtml:
#model Formulario.Pages.EditarModel
...
<h1>#Model.ff.IP</h1>
#foreach (var i in Model.ff.items)
{
<div>#i</div>
}
<button type="submit">Enviar</button>
The items are correctly output. But the complete object does not go to the OnPost.
The problem is: The model is not coming fully populated on the OnPost.
How to receive the full object that was created on the OnGet, plus the changes made by the user on the form, on the post to OnPostAsync() ?
The BindProperty attribute is used to inform ASP.NET Core that the values that the form submitted should be mapped to the specified object. In your case you set the values for the ff property but you do not have the equivalent input values so that ASP.NET Core will get these values in order to store them back to the ff property.
In order to make it work you will have to replace your razor code with the following code:
<form method="post">
<h1>#Model.ff.IP</h1>
<input asp-for="#Model.ff.IP" type="hidden" /> #* create a hidden input for the IP *#
#for (int i = 0; i < Model.ff.items.Count(); i++)
{
<input asp-for="#Model.ff.items[i]" type="hidden" /> #* create a hidden input for each item in your list *#
<div>#Model.ff.items[i]</div>
}
<button type="submit">Enviar</button>
</form>
Very important. To make this work you can not use the foreach loop because ASP.NET core will not be able to find the values. You will have to use a for loop.
The inputs that I added are hidden because I guess you do not want them to be visible but you can remore the type="hidden" so that you will be able to see them. Every change that you make to these inputs will be submitted to the OnPostAsync method.

ASP.NET MVC 4 - ListBoxFor, send selectedValue in ActionLink

I have a list of model. I want to retrieve the listBoxSelectedValue to send it in my actionLink to edit it.
This is my view :
#using (Html.BeginForm())
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis,"ID","Name", Model.SelectedApplis))<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})<br/>
#Html.ActionLink("Delete","Delete","Application")<br/>
}
I created a class "ListBoxApplication" with the List which will contain the selectedValue of the ListBox.
public class ListBoxApplication
{
public IEnumerable<int> SelectedApplis { get; set; }
public ListBoxApplication()
{
SelectedApplis = new List<int>();
}
}
I have 2 controllers : Application and Home
In HomeController, I created the model ListBoxApplication which contain the List. In my ViewBag.Applis, i have all my ApplicationModel.
public ActionResult Index()
{
ListBoxApplication listeApplis = new ListBoxApplication();
ViewBag.Applis = ApplicationModels.GetListApplications();
return View(listeApplis);
}
In my ApplicationController :
public ActionResult Edit(ListBoxApplication listAppId)
{
// I WANT TO RETRIEVE MY listAppId HERE, but it is always 'null'
return View();
}
So I think my problem is in the actionLink :
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})
Me Edit Method is not is the actual controller (Home/Index). I need to send the selectedValue of my ListBox in my actionLink to (Application/Edit).
The listAppId is always 'null'. It doesn't retrieve the value... Is there a mistake in my actionLink ?
Thanks for advance
I don't believe that action links will trigger a postback to the server. Try this instead:
#Html.ActionLink("Delete","Delete","Application")<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#using (Html.BeginForm("Detail","Application"))
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis)) //not sure what the other params you had here were for, but it should work like this
<br/>
<input type="submit" name="Edit" value = "Edit"/>
#*added in response to comment*#
<input type="submit" name="Delete" value = "Delete"/>
<input type="submit" name="Add" value = "Add"/>
}
If you plan on having all of those buttons post back to the server, you could also use ajax (and javascript) to accomplish this same goal, without needing to write out a form for each individual button. Both ways would work just fine, multiple forms is technically easier though.
public ActionResult Detail(ListBoxApplication listAppId, bool Edit, bool Add, bool Delete)
{
if(//check your bools here){
}
return View();
}