How do Delete view and action get the model id? - asp.net

In a standard MVC app, scaffolding controller with views gives a Delete view with this Razor form:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
#Html.ActionLink("Back to List", "Index")
</div>
}
Nowhere in the Delete view is any id field, hidden or not.
Then the controller for that view has this action:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(long id)
{
....
}
Where does this action get its id parameter value from? Is it somehow extracted from the only form value posted, the anti-forgery token __RequestVerificationToken, during some sort of model binding?

The id is passed as parameter when you redirected to the Delete action:
[HttpGet]
public ActionResult Delete(long id)
{
return View();
}
and since it is part of the original url (look at your browser address bar at the moment the Delete view is displayed) it will be preserved by the Html.BeginForm() helper - now look at the generated HTML markup and you will see this:
<form action="/somecontroller/delete/123" method="post">
...
</form>
That's where the id is coming from - the action of the generated form.

Because you have a GET method with a signature
public ActionResult Delete(long id)
and you using the default route (or at least a route definition containing /{id})
url: "{controller}/{action}/{id}"
When you navigate to that method, say using /yourController/Delete/10, the value of id is 10, and that is added to the action attribute of the <form> tag generated by your Html.BeginForm() method. When you submit the form, the value of the id parameter is bound for the route value in the forms action attribute (the DefaultModelBinder reads values from the form collection (any inputs you might have) as well as route values and query strings (among others)

Related

Razor Pages - Returning Model Value

I'm new to Razor pages and having trouble with model binding back to the view.
I'm using VS2019 version 16.0.4.
This is my PageModel:
public class IndexModel : PageModel
{
[BindProperty]
public int PageIndex { get; set; }
public IActionResult OnPost()
{
PageIndex++;
return Page();
}
}
And my View:
#page
#model IndexModel
<form method="post">
<div class="form-group">
<label asp-for="PageIndex"></label>
<input asp-for="PageIndex" class="form-control" />
<span class="text-danger" asp-validation-for="PageIndex"></span>
<button type="submit" class="btn btn-primary">Increment</button>
</div>
</form>
I would expect the value displayed in the input control to be incremented on each click - but it remains at zero. The binding into the controller seems to work ok. If I enter a value of "5" and click the button then a breakpoint shows me that a value of 5 is received before being incremented to 6. However, the incremented value does not get reflected back to the view.
Where did I go wrong?
Model binding takes values from the HTTP request and binds them to handler method parameters or PageModel properties. It is not two-way, and does not then bind those values back to the controls where the values originated.
You need to explicitly set the value attribute of the input to see the behaviour that you expect:
<input asp-for="PageIndex" value="#Model.PageIndex" />

How secure the EntityId in hidden field for Editing Form in Asp.Net Core MVC?

I'd like to create the form for editing some Entity (for example a post) in the database using the Entity Framework Core.
I want to protect the value PostId in the hidden field before rewriting to another value from the browser. I'm wondering about checking the user permissions before updating but I want to create some encryption/signing or something like that.
How can I encrypt or sign the PostId and in the controller decrypt or validate it?
I've created the example form for editing the post like this:
Entity - Post:
public class Post
{
[Key]
public int PostId { get; set; }
[Required]
[StringLength(40)]
public string Title { get; set; }
}
Controller - PostsController with Edit method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("PostId,Title")] Post post)
{
if (ModelState.IsValid)
{
//Update method
}
return View(post);
}
Form for editing:
#model EFGetStarted.AspNetCore.NewDb.Models.Post
#{
ViewBag.Title = "Edit Post";
}
<h2>#ViewData["Title"]</h2>
<form asp-controller="Posts" asp-action="Edit" method="post" asp-antiforgery="true" class="form-horizontal" role="form">
<div class="form-horizontal">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="PostId" type="hidden" />
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
</div>
</form>
By encrypting it you don't get any real business value and if the intent is so prevent one user to edit/modify posts he has no access to, you should do it in the backend by following the "Never trust the client" principle and always validate input on the server.
Easiest way to do is to use only the post ID from the model posted in and validate if the user has permissions to modify it. For this the new policy based systems offers resource based permissions which are well documented and can be used to validate the permissions.
Once done, passed take over the values and save the changes.
Also you shouldn't use persistence models inside the views, they easily break your API or your forms when the you change the database layout and navigation properties may cause issues (circular references etc.); especially later on, when lazy loading is implemented (lazy loading can't happen async as its inside a property, so the db call will block the thread).
Take a look at Sergey Akopov's blog post where he proposes a mechanism to deal with this scenario within ASP.NET MVC. His solution is to write a Html Helper that can be called within your view to generate a hidden input to accompany each input that you wish to make "tamper proof". This hidden input contains an encrypted copy of the value that you want to be tamper proof. When the form is posted, the server checks that the posted value and accompanying encrypted value still match - he writes a filter attribute which is applied to the corresponding controller action to perform this check. This adds an extra layer of "never trust the client" security.
Another example here has an interesting discussion (in the comments) around the potential security flaws inherent in this approach - The main one being that a determined attacker could "farm" valid combinations of secure field and encrypted value from their editing sessions, and subsequently use these farmed values to post tampered data with future edits.

How does #Html.BeginForm() work? and search result in Microsoft ASP.Net MVC 5 tutorial?

I am working on MVC 5 Asp.Net and following this tutorial. I am wondering how the heck does this fetch the result when I click the Filter button?
There comes a point where this code is added in Movie/view/Index.cshtml
#using (Html.BeginForm())
{
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
Now as far as I know, it creates a textbox and a button on screen. But how is this button calling the search(index) function and passing the value of textbox in the function, I could not get this.
It's not a stupid question. #html.BeginForm() works like this. It has some parameters you could add to it like Action Controller FormType htmlAttributes. The way it works is that if you leave it empty it will look for a post action with the same name that on the page you are now, for example if you are in on the login page, it will look for a login post action. I always write what action and controller I want it to access.
#Html.BeginForm("AddUser", "Admin", FormMethod.Post, new { #class = "my_form"}) {
}
So your post action should accept parameters that your form contains, and that can be a Model ie a Product, ViewModel or single string parameters. In your case with the search your action should look like
[HttpPost]
public ActionResult Search(string SearchString)
{
//do something here
}
Please note here, for the search string to be passed into the method. The name of the <input> has to be the same as the parameter your action takes. So our form should be like this
#using (Html.BeginForm("Search", "YOUR CONTROLLER", FormMethod.Post)){
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
Hope this brings clarity.

SPRING MVC post method call not working

By JSP has below :
<h2>Student Information</h2>
<form:form method="POST" action="/HelloWeb/addStudent">
<table>
and my java controller code has below
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student,
ModelMap model) {
when i try to hit the post doesnt work i,e /HelloWeb/addStudent,
I tried making both places /HelloWeb/addStudent or just /addStudent that doesnt work.
FYI : HelloWeb here is the DispatchServletName given in web,xml
I am trying example given in site
http://www.tutorialspoint.com/spring/spring_mvc_form_handling_example.htm
I apologize if i am asking very basic easisest issue, BUt tried this # late nite and fed up so requesting ppl to help/suggest
The attribute in jsp require modelAddribute or commandName which is the class instance of the domain object. You did not specify it. So the
<form:form method="POST" modelAttribute="SpringWeb" action="/HelloWeb/addStudent">.
There is a standard way to do form post submission in spring. You need to do GET request mapping to map/bind the Student table with jsp, and POST mapping to submit jsp form data. An example in your case would be.
#RequestMapping(value = "/addStudent", method = RequestMethod.GET)
public String addStudent(#ModelAttribute("SpringWeb") Student student) {
return "addstudentJsp"; // your jsp page name where the spring:form is placed
In jsp page do this
<h2>Student Information</h2>
<form:form modelAttribute="SpringWeb">
<form:input id="name" path="name" type="text" class="form-control" />
<form:errors path="name" cssClass="text-danger"></form:errors>
// your student fields
<button type="submit">submit</button>
</form:form>
Now again in your controller have a post request method like
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student, #BindingResult result) {
//Call to your data persistence layer like StudentService
}
The modelAttribute does the binding for you
I faced same problem, I just rename my project "HelloWeb" and the problem was solved.

ASP.Net MVC 4 Form with 2 submit buttons/actions

I have a form in ASP.Net and razor.
I need to have two ways of submitting said form: one that goes through the Edit action, and another that goes through the Validate action.
How should I go about doing this?
I don't mind using JavaScript for this.
EDIT:
Using the custom attribute I get this error.
The current request for action 'Resultados' on controller type 'InspecoesController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Validar(System.Collections.Generic.ICollection1[Waveform.IEP.Intus.Server.Web.ViewModels.ResultadoViewModel]) on type Waveform.IEP.Intus.Server.Web.Controllers.InspecoesController
System.Web.Mvc.ActionResult Resultados(System.Collections.Generic.ICollection1[Waveform.IEP.Intus.Server.Web.ViewModels.ResultadoViewModel]) on type Waveform.IEP.Intus.Server.Web.Controllers.InspecoesController
That's what we have in our applications:
Attribute
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
Actions decorated with it:
[HttpParamAction]
public ActionResult Save(MyModel model)
{
// ...
}
[HttpParamAction]
public ActionResult Publish(MyModel model)
{
// ...
}
HTML/Razor
#using (#Html.BeginForm())
{
<!-- form content here -->
<input type="submit" name="Save" value="Save" />
<input type="submit" name="Publish" value="Publish" />
}
name attribute of submit button should match action/method name
This way you do not have to hard-code urls in javascript
You can do it with jquery, just put two methods to submit for to diffrent urls, for example with this form:
<form id="myForm">
<%-- form data inputs here ---%>
<button id="edit">Edit</button>
<button id="validate">Validate</button>
</form>
you can use this script (make sure it is located in the View, in order to use the Url.Action attribute):
<script type="text/javascript">
$("#edit").click(function() {
var form = $("form#myForm");
form.attr("action", "#Url.Action("Edit","MyController")");
form.submit();
});
$("#validate").click(function() {
var form = $("form#myForm");
form.attr("action", "#Url.Action("Validate","MyController")");
form.submit();
});
</script>
If you are working in asp.net with razor, and you want to control multiple submit button event.then this answer will guide you. Lets for example we have two button, one button will redirect us to "PageA.cshtml" and other will redirect us to "PageB.cshtml".
#{
if (IsPost)
{
if(Request["btn"].Equals("button_A"))
{
Response.Redirect("PageA.cshtml");
}
if(Request["btn"].Equals("button_B"))
{
Response.Redirect("PageB.cshtml");
}
}
}
<form method="post">
<input type="submit" value="button_A" name="btn"/>;
<input type="submit" value="button_B" name="btn"/>;
</form>
Here is a good eplanation:
ASP.NET MVC – Multiple buttons in the same form
In 2 words:
you may analize value of submitted button in yout action
or
make separate actions with your version of ActionMethodSelectorAttribute (which I personaly prefer and suggest).
With HTML5 you can use button[formaction]:
<form action="Edit">
<button type="submit">Submit</button> <!-- Will post to default action "Edit" -->
<button type="submit" formaction="Validate">Validate</button> <!-- Will override default action and post to "Validate -->
</form>
<input type="submit" value="Create" name="button"/>
<input type="submit" value="Reset" name="button" />
write the following code in Controler.
[HttpPost]
public ActionResult Login(string button)
{
switch (button)
{
case "Create":
return RedirectToAction("Deshboard", "Home");
break;
case "Reset":
return RedirectToAction("Login", "Home");
break;
}
return View();
}
We can have this in 2 ways,
Either have 2 form submissions within the same View and having 2 Action methods at the controller but you will need to have the required fields to be submitted with the form to be placed within
ex is given here with code Multiple forms in view asp.net mvc with multiple submit buttons
Or
Have 2 or multiple submit buttons say btnSubmit1 and btnSubmit2 and check on the Action method which button was clicked using the code
if (Request.Form["btnSubmit1"] != null)
{
//
}
if (Request.Form["btnSubmit2"] != null)
{
//
}

Resources