I have the following form that is trying to let a User SignIn:
<form method="post" asp-controller="Auth" asp-action="SignIn">
<div class="form-group row">
<div class="col">
<input class="form-control" type="email" placeholder="Email" id="email-input" name="email">
</div>
</div>
<div class="form-group row">
<div class="col">
<input class="form-control" type="password" placeholder="Password" id="password-input" name="password">
</div>
</div>
<div class="form-group row">
<div class="col">
<button class="btn btn-success" type="submit" name="button" style="width: 100%;">Login</button>
</div>
</div>
</form>
As you can see the form is using a Controller named Auth and an action on the Controller named SignIn. Here is a sample of the Controller:
public class AuthController : Controller
{
public IActionResult Index()
{
return View(new SignInViewModel());
}
[HttpPost]
public async Task<IActionResult> SignIn(SignInViewModel model)
{
if (ModelState.IsValid)
{
if (await _userService.ValidateCredentials(model.Email, model.Password, out var user))
{
await SignInUser(user.Email);
return RedirectToAction("Index", "Home");
}
}
return View(model);
}
}
I've set a breakpoint on the line if (ModelState.IsValid) and it never gets hit. When I click the login button the page simply refreshes. I really cannot see why this breakpoint is not being hit when the Controller and the Action seem OK to me.
EDIT: SignInViewModel:
public class SignInViewModel
{
[Required(ErrorMessage = "Please Enter an Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Please Enter a Password")]
public string Password { get; set; }
}
Snippet from Chrome:
So I've made a new project in an attempt to replicate the issue and it works perfectly for me.
I'm assuming you're not using Areas also? If you are using areas, add this attribute to your controller class:
[Route("AreaNameHere")]
Although if you are able to get to the index page then this isn't the issue.
Are your breakpoints valid? Are symbols being loaded? It could be refreshing due to the ModelState being invalid or the details being invalid and so it's returning the same view. You could use fiddler to see if it's actually making a request at all.
EDIT:
Do you have this line "#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers" in your _ViewImports.cshtml file?
Related
I am trying to implement a simple CRUD using ASP.net Core as a beginner. My issue is with an HTTP post called DeletePlayer(). Please consider my code below:
Model:
public class Player
{
public int PlayerId { get; set; }
//... deleted unrelated code for shorter post ...
}
Controller:
public class PlayerController : Controller
{
private readonly DataContext _database;
public PlayerController(DataContext database)
{
_database = database;
}
//... deleted unrelated code for shorter post ...
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeletePlayer(int? id)
{
if (id == null || id == 0)
{
return NotFound();
}
var obj = _database.Players.Find(id);
if (obj == null)
{
return NotFound();
}
_database.Players.Remove(obj);
_database.SaveChanges();
return RedirectToAction("PlayerIndex");
}
}
View:
#model Player_Simulation.Models.Player
<form method="post" asp-action="DeletePlayer">
<input asp-for="PlayerId" hidden />
<div class="border p-3">
<div class="form-group row">
<h2 class="text-black-50 pl-3">Delete Player</h2>
</div>
<div class="row">
<div class="col-12">
<div class="form-group row">
<div class="col-6">
<label asp-for="PlayerName"></label>
</div>
<div class="col-6">
<label asp-for="PlayerRating"></label>
</div>
</div>
<div class="form-group row">
<div class="col-6">
<input asp-for="PlayerName" disabled class="form-control" />
</div>
<div class="col-6">
<input asp-for="PlayerRating" disabled class="form-control" />
</div>
</div>
<div class="form-group row">
<div class="col-8 offset-2 row">
<div class="col">
<input type="submit" class="btn btn-danger w-75" value="Delete" />
</div>
<div class="col">
<a asp-action="PlayerIndex" class="btn btn-success w-75">Back</a>
</div>
</div>
</div>
</div>
</div>
</div>
Graphics:
The DeletePlayer(int? id) I have in my PlayerController doesn't work and it gives me HTTP ERROR 404. However, if I change that to:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeletePlayer(Player obj)
{
_database.Players.Remove(obj);
_database.SaveChanges();
return RedirectToAction("PlayerIndex");
}
The delete works! So my questions are:
Why doesn't DeletePlayer(int? id) work while DeletePlayer(Player obj) work?
What must I do if I want DeletePlayer(int? id) to work?
I would appreciate anyone who could help me with my confusions!
1.Why doesn't DeletePlayer(int? id) work while DeletePlayer(Player obj) work?
Try to set a breakpoint in the DeletePlayer(int? id) HttpPost method, whether this breakpoint is hit when you click the Delete button?
If this breakpoint doesn't hit, it means there have a route error, the route doesn't find this action method, you need to check the request url and the action name.
If the breakpoint is hit, check the id value, perhaps the id is null, so the if condition will return a NotFound error. The reason for the empty id could be that the request url (route template) doesn’t contains the ID value, and the submit form doesn’t contains the id field. So, the id will be null.
You are using a form submit for your delete and that will attempt to send the entire model, so DeletePlayer(Player obj) work.
2.What must I do if I want DeletePlayer(int? id) to work?
The solution is what you see in the example, which is to add the ActionName("Delete") attribute to the DeletePlayer method. That attribute performs mapping for the routing system so that a URL that includes /Delete/ for a POST request will find the DeletePlayer method.
result:
From the above sample, we can see that in the Delete Get page, the request url is https://localhost:5001/Player/Delete/2, after clicking the delete button, the submit request url will be https://localhost:5001/Player/Delete/2, so in the HttpPost method, we can get the id value from the request url (route data), instead of the submitted form.
refer to:https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/details?view=aspnetcore-5.0
I created wizard with the jQuery-Steps lib with simple form for now. There is no Submit button. I submit form on finish step via jQuery.
I am already using jQuery calls all over the place, and I have included all scripts, and I needed form for uploading images and other stuff, it's easier with it.
Nothing happens, the controller action is not called.
I just getting redirected on start page with all this parameters in query string.
Like this:
https://localhost:44380/Dashboard?Name=Aaaa&Surename=Bbbb&City=Cccc
Dashboard is all made with Ajax and partial views. And from person option in menu I'm getting redirected on home view in Dashboard with all this parameters.
This is the form:
<form asp-controller="PersonSettings" asp-action="SaveForm" data-ajax="true" data-ajax-method="POST" id="personForm" class="steps-validation wizard-notification">
<!-- Step 1 -->
<h6>Person</h6>
<fieldset>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="Name">
Person Name :
<span class="danger">*</span>
</label>
<input autocomplete="off" type="text" class="form-control required" id="Name" name="Name">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="Surname">
Person Surname:
<span class="danger">*</span>
</label>
<input autocomplete="off" type="url" class="form-control required" id="Surname" name="Surname">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="City">
Person City:
<span class="danger">*</span>
</label>
<input autocomplete="off" type="url" class="form-control required" id="City" name="City">
</div>
</div>
</div>
</fieldset>
</form>
This is the Model:
public class PersonDto {
public string Name { get; set; }
public string Surname { get; set; }
public string City { get; set; }
}
Here is Action in PersonSettings controller:
[Route("SaveForm")]
[HttpPost]
public IActionResult SaveForm(PersonDto Person) {
//Do something with person model
return Ok();
}
And on finish button I have jQuery submit called on this form:
$('#personForm').submit();
EDIT:
I tried with this parameters:
<form action="PersonSettings/SaveForm" method="post" id="personFrom" class="steps-validation wizard-notification">
And this works.. it's good, but why does the first method not work? Because I need this in Ajax and finish method.
I didn't find any way to post image with form with classic Ajax call. Because I can't read image path (it's not exposed to the browsers).
I think that you didn't installed Tag Helpers. If it works with action attribute, it should work with Tag Helpers attribute. Because you're using Asp-Net-Controller attribute.
https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.TagHelpers
Please let me know.
I have an ASP.NET Core MVC app attempting to upload an IFormFile. However, the IFormFile is always null. None of the other solutions I've found have solved this issue. What am I doing wrong?
Model
public class EmailForm
{
[Display(Name = "Add a picture")]
[DataType(DataType.Upload)]
[FileExtensions(Extensions = "jpg,png,gif,jpeg,bmp,svg")]
public IFormFile SubmitterPicture { get; set; }
}
Controller
public async Task<ActionResult> Contribute([Bind("SubmitterPicture")] EmailForm model)
{
if (ModelState.IsValid)
{
//Do some stuff
}
}
View
<form method="post" asp-action="Contribute" asp-antiforgery="true" enctype="multipart/form-data" >
<div class="form-group" >
<div class="col-md-2">#Html.LabelFor(m => m.SubmitterPicture)</div>
<div class="col-md-4">
<input type="file" name="SubmitterPicture" id="SubmitterPicture" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Contribute" />
</div>
</div>
<form method="post" enctype="multipart/form-data">
</form>
enctype="multipart/form-data"
The multipart form data is the key.
You can alternatively get the file from the HttpContext.Request.Form.Files and get rid of the IFormFile interface in your model. I recommend this method as I believe that files have nothing to do with data models.
The example would be:
public IActionResult Index()
{
//Please note that if no form data is posted
// HttpContext.Request.Form will throw an exception
if (HttpContext.Request.Form.Files[0] != null) {
var file = HttpContext.Request.Form.Files[0];
using (FileStream fs = new FileStream("Your Path", FileMode.CreateNew, FileAccess.Write, FileShare.Write)) {
file.CopyTo(fs);
}
}
return View();
}
If this method also fails, that means there is something wrong with the multipart request.
After sitting with the same problem for hours I found the solution.
The Problem:
Submitting a single input in a form.
The Conclusion:
Basically in your html form this won't work if you only have one input besides the submit button. My property was no longer null as soon I added another property on my viewModel and another input in my form.
I hope this helps someone else.
Razor Page HTML:
#page
#model SomeModel
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input asp-for="Input.SingleInput" type="file" class="form-control" />
<input asp-for="Input.AnotherInput" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Upload" />
</div>
</div>
</form>
Razor Page Code Behind:
public class SomeModel: PageModel
{
[BindProperty]
public SomeViewModel Input { get; set; }
public async Task OnGetAsync()
{
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
// This won't be null.
var showMeSomething = Input.SingleInput;
return RedirectToPage();
}
}
SomeViewModel:
public class SomeViewModel
{
public IFormFile SingleInput{ get; set; }
public string AnotherInput{ get; set; }
}
I found that by providing names on my html form, I was able to map to parameters (ASP.NET Core 2.1):
Client side:
<form method="post" enctype="multipart/form-data" action="...">
<input type="file" name="myFile" required />
<input type="password" name="myPass" required />
<input type="submit" value="post" />
</form>
Server side:
[HttpPost()]
public async Task<IActionResult> Post(IFormFile myFile, [FromForm]string myPass)
{
//...
}
Similarly, if anyone working in .Net Core 2.1 and are using asp-for inorder bind the model for your input elements, then do not give name & id properties for that <input> elements. Ideally, the InputTagHelper upon the rendering on the browser generates the name and id properties with their values. If you give the same value for name & id w.r.t Model class, then everything works fine. Else, system doesn't know to which model property it should bind.
Better approach is not to give id and name on the <input>
Below is the sample code.
<form id="uploadForm" enctype="multipart/form-data" name="uploadForm" asp-action="UploadExcel" method="post">
<div class="form-group form-group-lg form-group-sm row ">
<div class="col-sm-12 col-md-10 col-lg-10 uploadDiv" style="display: flex !important">
<label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">File Name :</label>
<input asp-for="FileName" class="form form-control fileName"
type="text"
placeholder="Enter your file name" />
<!--File upload control-->
<label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">Attachment :</label>
<input asp-for="File" required class="form-control" type="file" placeholder="File Name" />
</div>
</div>
<div class=" form-group form-group-lg form-group-sm row">
<span asp-validation-for="FileName" class="text-danger"></span>
</div>
<div class=" form-group form-group-lg form-group-sm row">
<small>Please upload .xls or .xlxs or json or xml formatted files only</small>
</div>
<div class="form-group form-group-lg form-group-sm row">
<div class="col-sm-12 col-md-10 col-lg-10">
<input type="submit" class="btn btn-primary" name="submit" id="fileUploadButton" value="Upload" />
<input type="reset" class="btn btn-Gray" name="result" id="resetButton" value="Reset" />
</div>
</div>
<a asp-action="DownloadTemplate" asp-controller="Download" title="Click to download template">Import Batch Transactions Template</a>
</form>
Model.cs
public class ExcelUploadViewModel
{
/// <summary>
/// Gets or Sets the FileName
/// </summary>
[Required(ErrorMessage = "FileName is required")]
public string FileName { get; set; }
[Required(ErrorMessage = "File is required")]
[DataType(DataType.Upload)]
[FromForm(Name = "File")]
public IFormFile File { get; set; }
}
Upon Submit
Thank you.
Your code looks perfectly fine and it should work as long as you are using the same version of code you posted on the question. I have a very strong feeling that you are getting false for the ModelState.IsValid expression and hence seeing the some sort of unexpected behavior.
The FileExtensions data annotation is supposed to be used with String type properties, not with IFormFile type properties. Because of this reason, the IsValid returns false.
So remove that from your view model.
public class EmailForm
{
[Display(Name = "Add a picture")]
[DataType(DataType.Upload)]
public IFormFile SubmitterPicture { get; set; }
}
Here is a relevant GH issue and explanation from one of the team member, for your reference.
FileExtensions Data annotation invalid ModelState #5117
I had this issue and the fix was to make sure the element of "input type="file"" had an id and name set. Then, in the controller, set the IFormFile parameter name to be exactly the same, case as well.
G-luck
Adding the below to my controller fixed the issue for me. It was because my file was large. Adjust the limits as needed.
[RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)]
I am trying to get data from a textbox in a form inside view to controller in asp.net-mvc.
My requirement is to compare Token_No. with some id and
Password with some existing password. How to get get those values at controller side on button click inside if condition.
I am not using razor syntax so how to accomplish this situation without using razor syntax and strongly bind with model.
Code in view
<form name="ctl00" id="ctl00" action="HomeController/Index" method="post" data-dpmaxz-fid="1">
<div class="input-group">
<span class="input-group-addon"> <i>Token_No.</i></span>
<div class="form-group is-empty">
<input name="txtToken" class="form-control" id="txtToken" type="text" placeholder="Token No..." data-dpmaxz-eid="5">
<span class="material-input"></span>
</div>
</div>
<div class="input-group"> <span class="input-group-addon">
<i class="material-icons">lock_outline</i> </span>
<div class="form-group is-empty">
<input name="txtPswd" class="form-control" id="txtPswd" type="password" placeholder="Password..." data-dpmaxz-eid="5">
<span class="material-input"></span>
</div>
</div>
<div class="footer text-center">
<input name="btn_Login" class="btn btn-primary" id="btn_Login" type="submit" value="Login" data-dpmaxz-eid="6">
</div>
</form>
Code in Controller
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(EmpDetails model)
{
if (model.token_no.="197418")
{
}
return View();
}
}
you can generate the view using controller method & create a strongly typed view. then you can choose a model class & select scaffold template as what you want..you can select create,edit,delete..etc as you want.then you can access the text box values using form collection.
[HttpPost]
public ActionResult authenticateUser(FormCollection formCollection)
{
int token = int.Parse(formCollection["txtToken"]);
string pswd = formCollection["txtPswd"];
return View();
}
I am experiencing very strange behavior. I am creating a simple User creation module. I have created a very simple models,
public class CreateUser
{
[Required]
public string Email { get; set; }
[Required]
[Display(Name = "User name")]
public string Username { 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 new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
When I access the view, the following exception message is displayed,
An exception of type 'System.Web.HttpException' occurred in System.Web.dll but was not handled in user code
Additional information: Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
and sometimes the same page gives this exception,
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
And sometime just this error,
An exception of type 'System.Web.HttpException' occurred in System.Web.dll but was not handled in user code
I have isolated the problem but I am unable to understand what is problem with my view. Below is my View,
#model Practice_Project.Models.Accounts.CreateUser
#{
ViewBag.Title = "Register";
}
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new { #class = "validator-form form-horizontal", role = "form" }))
{
<div class="form-group">
<label for="Email" class="col-sm-3">Email</label>
<div class="col-md-9">
<input type="text" class="form-control inputmask" id="Email" name="Email" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="username" class="col-sm-3">User Name</label>
<div class="col-md-9">
<input type="text" class="form-control inputmask" id="Username" name="Username" placeholder="User Name">
</div>
</div>
<div class="form-group">
<label for="Password" class="col-sm-3">Password</label>
<div class="col-md-9">
<input type="password" class="form-control" id="Password" name="Password" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="ConfirmPassword" class="col-sm-3">Confirm Password</label>
<div class="col-md-9">
<input type="password" class="form-control" id="ConfirmPassword" name="ConfirmPassword" placeholder="Re-Type your Password">
</div>
</div>
<div class="form-group">
<div class="col-md-7">
<button type="submit" class="btn btn-success" value="Validate">Sign In</button>
<span class="pull-right">
<small>Not Register ?</small>
<span>
Signup Now
</span>
</span>
</div>
</div>
}
When I remove this last piece of code from the form then the exception does not occur,
<div class="form-group">
<div class="col-md-7">
<button type="submit" class="btn btn-success" value="Validate">Register</button>
<span class="pull-right">
<small>Already Registered?</small>
<span>
SignIn Now
</span>
</span>
</div>
</div>
I don't see anything wrong with this piece of code which is when removed the error is gone. In troubleshooting options it is mentioned that it might be due to Infinite loop but how?
Edit:
The controller is very simple and has just two methods for now as I am adding as I move forward. The following is the code for controller,
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
}
Note I am trying to generate a link on the Register page that navigates to the Login page.
#Html.Action("Login", "Account") is a method that calls the Login() method of AccountController and renders the partial view it returns. In order to generate a url for a link, you need to use #Url.Action("Login", "Account") although I suggest you use
#Html.ActionLink("SignIn Now", "Login", "Account", null, new { #class = "btn btn-info" })
The reason for the exception (as identified in the comments) was that you had not yet created a view for the Login method (the Login method is trying to return a view which does not exist).
However there are other issues with you code. Your properties are decorated with validation attributes, but because your manually generating the html and omitting the data-val-* attributes, you will not get client side validation, and because you do not have #Html.ValidationSummary() or #Html.ValidationMessageFor() helpers in the form, server side validation errors will not be displayed. Remove the manual html for each property and replace with
<div class="form-group">
#Html.LablFor(m => m.Email, new { #class = "col-sm-3" })
<div class="col-md-9">
#Html.TextBoxFor(m => m.Email, new ( placeholder="Email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div>
and ditto for the other properties. Also include the relevant files for client side validation (typically by including #Scripts.Render("~/bundles/jquery") and #Scripts.Render("~/bundles/jqueryval") in the view).
Finally, the typical approach is that you decorate methods required authorization with the [Authorize] attribute. If an unauthorized user navigates to the method, they are automatically redirected to the login page which may include a link to the register page, both of which are used to authorize the user. Having a link in the Register page that navigates to the Login page seems unusual (why would a user that is already registered navigate to the register page?)