I am create a login page in C# (running at port https://localhost:7139/account/login). Now I want to protect an angular application(running at port https://localhost:4200) from un-authorized user. For now I hard coded the login credentials.I tried to protect the angular application by creating a resolver in my angular application.
My C# (login.cshtml) code
<div class="container border" style="padding:20px">
<form method="post">
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<div class="form-group row">
<div class="col-3">
<label asp-for="Credential.Username"></label>
</div>
<div class="col-5">
<input type="text" asp-for="Credential.Username" class="form-control" />
</div>
<div class="text-danger" asp-validation-for="Credential.Username"></div>
</div><br />
<div class="form-group row">
<div class="col-3">
<label asp-for="Credential.Password"></label>
</div>
<div class="col-5">
<input type="password" asp-for="Credential.Password" class="form-control" />
</div>
<div class="text-danger" asp-validation-for="Credential.Password"></div>
</div>
<div class="form-group row">
<div class="col-3">
<input type="submit" class="btn btn-primary" value="Login" />
</div>
</div>
</form>
</div>
My C# controller code
using Identity.Pages.Account;
using Microsoft.AspNetCore.Mvc;
namespace Identity.Controller
{
[ApiController]
public class LoginController : ControllerBase
{
[HttpPost]
[Route("account/login")]
public async Task<IActionResult> OnPostAsync([FromForm] Credential credential)
{
if (credential.Username == "badruddin" && credential.Password == "khan#123"
{
return Redirect("http://localhost:4200/");
}
else
{
return NotFound("User Not Found");
}
}
}
}
My (Angular) services.service.ts code
export class serviceService {
check = true;
constructor() { }
loginUrl(): any {
if(this.check == true){
this.check = false;
return window.location.href = 'https://localhost:7139/account/login';
}
}
}
my (Angular) resolver code is here
export class ResolveResolver implements Resolve<boolean> {
constructor(private service:MyserviceService){}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
this.service.loginUrl()
return of(true);
}
}
my (Angular) app-routing.ts code
const routes: Routes = [
{path:'', component:AppComponent, pathMatch:'full', resolve: {redirection: ResolveResolver} }
];
Now when I tried to open angular application it redirects to me at login page(running at https://localhost:7139/account/login) and when hitting the login button after putting login credentials it redirects me to angular application(running at https://localhost:4200) for an instance and then automatically redirects me to the login page(https://localhost:7139/account/login)
I want to stay at angular application once I validate the login credentials.
This time I am not using any cookies or JWT token.
I am new In the field of coding.Can any help me how I can resolve this issue.
I have a Blazor form using .NET 6 and have it working with an API PUT endpoint that can update the database upon clicking a button. This takes all of the bound input fields in the form and submits them into the payload.
I've modified my endpoint such that if payload is missing any attributes, it sends null and doesn't update the particular database field, essentially ensuring that only the sent changes are the items that get updated.
However, I'm struggling to find an example in Blazor where the submit only sends input fields on the form that were modified. I'm new to Blazor and my original implementation just had a button that had an #onclick event, which then called the endpoint, passing the object that has data bound in the form. This works but I'd prefer that I understand how to only send the data modified to not have unrequired overwrites.
Here's a mockup of what I have (truncated for readability):
#inject IDataSetService DataSetService
#page "/GameData/Data/DataSetEdit"
#page "/GameData/Data/DataSetEdit/{IDRouteParam:int}"
<PageTitle>DataSet Edit</PageTitle>
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<!-- Card -->
<div class="card">
<div class="card-header">
</div>
<form>
<div class="card-body">
<div class="form-group">
<label for="ID">ID</label>
<input type="text" class="form-control" id="ID" placeholder="12345" value="#dataSet.ID">
</div>
<div class="form-group">
<label for="IsActive">Is Active?</label>
<input type="text" class="form-control" id="IsActive" placeholder="IsActive" #bind-value="dataSet.IsActive">
</div>
<div class="form-group">
<label for="IsDeleted">Is Deleted?</label>
<input type="text" class="form-control" id="IsDeleted" placeholder="IsDeleted" #bind-value="dataSet.IsDeleted">
</div>
<div class="card-footer">
<button type="submit" class="btn btn-info" #onclick="() => UpdateDataSet()">Save</button>
<button type="submit" class="btn btn-danger">Cancel</button>
</div>
</form>
</div>
<!-- /.Card -->
}
</div>
</section>
</div>
#code {
private DataSet? dataSet = null;
private string message = string.Empty;
private bool ActiveChecked = false;
private bool DeletedChecked = false;
[Parameter]
public int IDRouteParam { get; set; }
protected override async Task OnParametersSetAsync()
{
message = "Loading DataSet.. ";
var result = await DataSetService.GetDataSetByID(IDRouteParam);
if (!result.Success)
{
message = result.Message;
}
else
{
dataSet = result.Data;
}
}
async void UpdateDataSet()
{
dataSet= await DataSetService.UpdateDataSet(dataSet);
}
}
I have been reading on changing this to an EditForm along with an OnValidSubmit, but I haven't come across an example that shows how to detect fields that were changed and how to use those to build the payload to send to the API.
I am trying to get the built in HTML5 validation working in my Blazor application but it just gets ignored.
This is my Razor code
<form class="row">
<div class="form-row">
<label for="exampleFormControlInput1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com" #bind="#details.Email">
</div>
<div class="form-row">
<label for="exampleFormControlInput1" class="form-label">Phone</label>
<input type="tel" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com" #bind="#details.Phone">
</div>
<div class="form-row">
<button type="submit" class="btn btn-primary" #onclick="SubmitForm">Send</button>
</div>
</form>
</div>
#code {
private ContactDetails details = new ContactDetails();
public async void SubmitForm()
{
var result = await Http.PostAsJsonAsync("Contact", details);
}
}
I know I can use Blazors validation, but I want to just use the built in HTML5 validation for this as it is a very basic form. How do I stop the SubmitForm code from being run if the HTML5 validation is triggered?
If I remove the code from the SubmitForm() method obviously nothing then happens when it enters that method and it goes back to the webpage and the html5 validation messages are displayed, however clearly the method is still being fired. Is there a way to stop the code running if there are html5 validation errors?
Okay, I have worked out how to do this.
Simply add and onsubmit event to the Form tag and then remove the onclick from the button.
<form class="row" #onsubmit="SubmitForm">
This then works as expected.
Now there are clearly lots of benefits to using the built in Blazor forms components but sometimes just working with standard html forms is needed and thankfully there is a solution to do that.
Use Regex by adding this using #using System.Text.RegularExpressions;
Valid email : komaei#live.com
Invalid emails : komaei , komaei# , komaei#live.c
<div class="form-group row">
<label for="#nameof(createModel.Email)" class="col-sm-2 col-form-label text-right">Email : </label>
<div class="col-lg-7 col-sm-10">
<InputText type="email" class="form-control" placeholder="Email" required
id="#nameof(createModel.Email)" #bind-Value="createModel.Email">
</InputText>
</div>
</div>
#code {
private Customer createModel = new();
private async Task SubmitForm()
{
var emailPattern = #"^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)#([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$";
Regex regex = new Regex(emailPattern);
if (regex.IsMatch(createModel.Email) == false)
{
await JsRuntime.InvokeVoidAsync("alert", "Email is not valid!");
return;
}
var response = await _http.PostAsJsonAsync("Customers/Create", createModel);
if (response.IsSuccessStatusCode) // 200
_navigationManager.NavigateTo("Customers");
else if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
await JsRuntime.InvokeVoidAsync("alert", response.Content.ReadAsStringAsync().Result);
}
}
I'm writing an application using Blazor server side and .Net 5. I want when I write text in the search box it will show results in new divs. However, the search results are not displayed. When I debug the code from service works fine.
#page "/"
#using MovieSearch.Service
#using MovieSearch.Data
#using MovieSearch.Helpness
#inject IOMDbService service
<div class="movie-search">
<div class="search-container">
<form action="/">
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button type="submit" #onclick="FindMovies"><i class="fa fa-search"></i></button>
</form>
</div>
</div>
<div class="movies">
#if(moviesResult != null)
{
if(!moviesResult.Success)
{
foreach(var error in moviesResult.Errors)
{
<p>#error.Message</p>
}
}
else
{
foreach(var movie in moviesResult.Value)
{
<div class="movie">
<p>#movie.Title</p>
<img src="#movie.Poster" />
</div>
}
}
}
</div>
#code
{
private string searchTerm;
private Result<IEnumerable<Movie>> moviesResult;
async Task FindMovies()
{
moviesResult = await service.FindMovies(searchTerm);
}
}
What can I do to view the results?
Try changing this part of your code from
<form action="/">
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button type="submit" #onclick="FindMovies"><i class="fa fa-search"></i></button>
</form>
to just this
<input type="text" placeholder="Find a movie" #bind-value="#searchTerm" />
<button #onclick="FindMovies"><i class="fa fa-search"></i></button>
So in other words, remove the form tags, as well as removing the type on the button. I believe that blazor is now actually trying to post the form to whatever action you specified in the form as opposed to the onclick handler you assigned to your button.
In Blazor I'm using modal dialogs for CRUD. When I open a modal to edit a record and I delete (for example) the user name and then directly click the Cancel button, form validation still kicks in. The modal does not close.
I need to click the Cancel button again to close the modal.
I know I can put the cancel button outside the EditForm, but then you'll see a validation message flashing before the dialog closes. And I want my cancel button next to my submit button in the modal footer.
Is there any way to override the form validation when I press the Cancel button? And I'd rather not want to use JavaScript Interop, just plain Blazor.
Working Code Example:
#page "/cancel"
#using System.ComponentModel.DataAnnotations;
<h3>Cancel Validation</h3>
<button type="submit" class="btn btn-primary" #onclick="Login">Login</button>
<hr />
<p>Status: #status</p>
#if (showModal)
{
<div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
<div class="modal-dialog shadow-lg bg-white rounded" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Login</h5>
</div>
<div class="modal-body">
<EditForm Model="user" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group row">
<label class="col-3 col-form-label">Email: </label>
<InputText class="col-8 form-control" #bind-Value="user.Email" />
</div>
<div class="form-group row">
<label class="col-3 col-form-label">Password: </label>
<InputText type="password" class="col-8 form-control" #bind-Value="user.Password" />
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary" #onclick="CancelSubmit">Cancel</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
}
#code {
public class UserLogin
{
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
}
UserLogin user = new UserLogin();
bool showModal;
string status;
protected override void OnInitialized()
{
showModal = false;
status = "Init";
// for demo purposes, if you delete user.Email in the login dialog, you'll need to press 'Cancel' 2 times.
user.Email = "user#example.com";
user.Password = "12345";
}
private void Login()
{
status = "Show Login Modal";
showModal = true;
}
private void HandleValidSubmit()
{
status = "Valid Submit";
showModal = false;
}
private void CancelSubmit()
{
status = "Cancelled";
showModal = false;
}
}
Today I found a much more simpler solution... just use a <div> instead of <button> :)
So just change your cancel button to:
<div type="button" class="btn btn-secondary" #onclick="CancelSubmit">Cancel</div>
The validation will not be triggered this way, but your serverside code will still be executed normally.
#Jaap, here's a solution that is based on the internals of Forms validation. I hope it'll satisfy you until a better solution is offered.
Forms validation support in Blazor, added to the EditContext object is performed on two level: object-level and field-level. When you click on the Submit button, the whole Model is validated. We've already seen that the Submit button works perfectly well, and does not allow you to submit unless the Model's fields' values are valid. When you hit the Cancel button and the Model's fields' values are valid, the dialog is closed without issue. But when one or more fields are having invalid values (as for instance, after clearing the email field), and you click on the Cancel button, a field-level validation instantly starts, before the code in the Cancel button's event handler has the slightest chance to do something. This behavior is by design and is repeated even when I used Fluent Validation instead of DataAnnotations validation. Conclusion: It is our limitations, not the system. We need to invest more time in learning Blazor.
The solution I propose is to disable a field-level validation when we click on the Cancel button, and thus instantly close the dialog without any validation taking place at all.
Note: My code is using Fluent Validation as I was experimenting with Fluent Validation, but the same can be done with DataAnnotations Validation as well. The code in both cases is almost identical, and has really nothing to do with Fluent Validation. Note that I have adapted the Fluent Validation sample code by Chris Sainty
UserLogin.cs
public class UserLogin
{
public string Email { get; set; }
public string Password { get; set; }
}
UserLoginValidator.cs
public class UserLoginValidator : AbstractValidator<UserLogin>
{
public UserLoginValidator()
{
RuleFor(user => user.Email).NotEmpty().WithMessage("You must enter an email address");
RuleFor(user => user.Email).EmailAddress().WithMessage("You must provide a valid email address");
RuleFor(user => user.Password).NotEmpty().WithMessage("You must enter a password");
RuleFor(user => user.Password).MaximumLength(50).WithMessage("Password cannot be longer than 50 characters");
}
}
FluentValidationValidator.cs
public class FluentValidationValidator : ComponentBase
{
[CascadingParameter] EditContext CurrentEditContext { get; set; }
[Parameter] public bool ShouldValidate { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext == null)
{
throw new InvalidOperationException($"{nameof(FluentValidationValidator)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. For example, you can use {nameof(FluentValidationValidator)} " +
$"inside an {nameof(EditForm)}.");
}
CurrentEditContext.AddFluentValidation(ShouldValidate);
}
}
EditContextFluentValidationExtensions.cs
public static class EditContextFluentValidationExtensions
{
public static EditContext AddFluentValidation(this EditContext editContext, bool shouldValidate)
{
if (editContext == null)
{
throw new ArgumentNullException(nameof(editContext));
}
var messages = new ValidationMessageStore(editContext);
editContext.OnValidationRequested +=
(sender, eventArgs) => ValidateModel((EditContext)sender, messages);
editContext.OnFieldChanged +=
(sender, eventArgs) => ValidateField(editContext, messages, eventArgs.FieldIdentifier, shouldValidate);
return editContext;
}
private static void ValidateModel(EditContext editContext, ValidationMessageStore messages)
{
var validator = GetValidatorForModel(editContext.Model);
var validationResults = validator.Validate(editContext.Model);
messages.Clear();
foreach (var validationResult in validationResults.Errors)
{
messages.Add(editContext.Field(validationResult.PropertyName), validationResult.ErrorMessage);
}
editContext.NotifyValidationStateChanged();
}
private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier, bool shouldValidate)
{
Console.WriteLine(fieldIdentifier.FieldName.ToString());
if (shouldValidate)
{
var properties = new[] { fieldIdentifier.FieldName };
var context = new FluentValidation.ValidationContext(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));
var validator = GetValidatorForModel(fieldIdentifier.Model);
var validationResults = validator.Validate(context);
messages.Clear(fieldIdentifier);
foreach (var validationResult in validationResults.Errors)
{
messages.Add(editContext.Field(validationResult.PropertyName), validationResult.ErrorMessage);
}
editContext.NotifyValidationStateChanged();
}
}
private static IValidator GetValidatorForModel(object model)
{
var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(model.GetType());
var modelValidatorType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.IsSubclassOf(abstractValidatorType));
var modelValidatorInstance = (IValidator)Activator.CreateInstance(modelValidatorType);
return modelValidatorInstance;
}
}
Cancel.razor
#page "/cancel"
#using System.ComponentModel.DataAnnotations;
<h3>Cancel Validation</h3>
<button type="submit" class="btn btn-primary" #onclick="Login">Login</button>
<hr />
<p>Status: #status</p>
#if (showModal)
{
<div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
<div class="modal-dialog shadow-lg bg-white rounded" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Login</h5>
</div>
<div class="modal-body">
<EditForm Model="user" OnValidSubmit="HandleValidSubmit">
#*<DataAnnotationsValidator />*#
<FluentValidationValidator ShouldValidate="false" />
#*<ValidationSummary />*#
<div class="form-group row">
<label class="col-3 col-form-label">Email: </label>
<InputText class="col-8 form-control" #bind-Value="user.Email" />
<ValidationMessage For="#(() => user.Email)" />
</div>
<div class="form-group row">
<label class="col-3 col-form-label">Password: </label>
<InputText type="password" class="col-8 form-control" #bind-Value="user.Password" />
<ValidationMessage For="#(() => user.Password)" />
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary" #onclick="CancelSubmit">Cancel</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
}
#code {
UserLogin user = new UserLogin();
bool showModal;
string status;
protected override void OnInitialized()
{
showModal = false;
status = "Init";
// for demo purposes, if you delete user.Email in the login dialog, you'll need to press 'Cancel' 2 times.
user.Email = "user#example.com";
user.Password = "12345";
}
private void Login()
{
status = "Show Login Modal";
showModal = true;
}
private void HandleValidSubmit()
{
status = "Valid Submit";
showModal = false;
}
private void CancelSubmit()
{
Console.WriteLine("CancelSubmit");
status = "Cancelled";
showModal = false;
}
}
Note that the FluentValidationValidator component has a property named ShouldValidate which we set to false in order to remove field-level validation. Please, follow the flow of execution it's very simple. I almost do nothing to solve the issue, which make me think that perhaps there's a shorter and better way to do it.
You may need to install Fluent Validation package...
Good luck...
So I've found something new here: ASP.NET Core Blazor forms and validation
The <ValidationSummary style="#displaySummary" /> is disabled by default and I enable it when there is an InvalidSubmit..
It's not an ideal solution because when a user (1) deletes the contents of a field then (2) clicks 'Submit' the validation will kick in but then if the users enters something valid in the same field (3) the user has to click 'Submit' twice. But for now I can live with this because this happens not so often.
The new working code:
#page "/cancel"
#using System.ComponentModel.DataAnnotations;
<h3>Cancel Validation</h3>
<button type="submit" class="btn btn-primary" #onclick="OpenDialog">Open Dialog</button>
<hr />
<div>#((MarkupString)status)</div>
#if (showModal)
{
<div class="modal" tabindex="-1" role="dialog" style="display:block" id="taskModal">
<div class="modal-dialog shadow-lg bg-white rounded" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Cancel Test</h5>
<button type="button" class="close" #onclick="CancelSubmit"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<div>
<EditForm Model="#user" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary style="#displaySummary" />
<div class="form-group row">
<label class="col-3 col-form-label">Email</label>
<InputText class="col-8 form-control" #bind-Value="user.Email" />
</div>
<div class="form-group row">
<label class="col-3 col-form-label">Password</label>
<InputText type="password" class="col-8 form-control" #bind-Value="user.Password" />
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary" #onclick="CancelSubmit">Cancel</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
</div>
}
#code {
public class UserLogin
{
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
}
private UserLogin user = new UserLogin();
bool showModal = false;
private string displaySummary = "display:none";
string status = "";
protected override void OnInitialized()
{
InitUser();
}
private void InitUser()
{
// for demo purposes, think of it as an 'edit dialog' in CRUD operation.
status += "Init User";
user.Email = "user#example.com";
user.Password = "12345";
displaySummary = "display:none";
}
private void OpenDialog()
{
status += "<br />Open User Dialog";
showModal = true;
}
private void HandleValidSubmit()
{
status += "<br />Valid Submit";
displaySummary = "display:none";
showModal = false;
}
private void HandleInValidSubmit()
{
displaySummary = "display:block";
status += "<br />Invalid Submit";
}
private void CancelSubmit()
{
status += "<br />Cancelled<br /><br />";
InitUser(); // for demo purposes so you can test it multiple times
showModal = false;
}
}
I put 2 handlers in my EditForm: one for OnValidSubmit (which should handle Cancel even if the form is valid), and another OnInvalidSubmit (to handle cancel when the form is invalid, so, some duplication but it works!). (Example shows using a Radzen card for the confirmation dialog, and my seemingly redundant Close and Confirm methods that I am still trying to figure out....).
<EditForm Model="#selectedUnit" OnValidSubmit="#Update" OnInvalidSubmit="#AreYouSure">
<Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator />
<Microsoft.AspNetCore.Components.Forms.ValidationSummary />
<button type="submit" #onclick="#(() => selectedUnit.Action = "Save")" class="btn btn-primary">Save</button>
<button #onclick="#(() => selectedUnit.Action = "Cancel")" class="btn btn-primary" >Cancel</button>
#code{
public async void AreYouSure()
{
// should use if to determine if they clicked Save or Cancel... this is the code for if Cancel //
await dialogService.OpenAsync("Are you sure?", ds =>#<RadzenCard Style="padding: 20px;">
<p Style="margin-bottom: 10px;">Changes will not be saved!</p>
<div class="row">
<div class="col-md-12">
<RadzenButton Text="Confirm Cancel Changes" Click="() => Confirm(true)" Style="margin-bottom: 10px; width: auto" />
<RadzenButton Text="Oops! Continue Input" Click="() => ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" Style="margin-bottom: 10px; width: auto" />
</div>
</div>
</RadzenCard>);
return;
}
private async Task Update()
{
if (selectedAppl.Action == "Cancel")
{
await dialogService.OpenAsync("Are you sure?", ds =>#<RadzenCard Style="padding: 20px;">
<p Style="margin-bottom: 10px;">Changes will not be saved!</p>
<div class="row">
<div class="col-md-12">
<RadzenButton Text="Confirm Cancel Changes" Click="()=> Confirm(true)" Style="margin-bottom: 10px; width: auto" />
<RadzenButton Text="Oops! Continue Input" Click="()=> ds.Close(false)" ButtonStyle="ButtonStyle.Secondary" Style="margin-bottom: 10px; width: auto" />
</div>
</div>
</RadzenCard>);
return;
};
public void Confirm(bool confirmed)
{
if (confirmed)
{ ReturnToList(); }
else
{
return;
}
}
void Close(bool confirmed)
{
if (confirmed)
{ ReturnToList(); }
}
public void ReturnToList()
{
NavigationManager.NavigateTo("/Data/Counter");
}