Model Data not binding to textboxfor after insertion in MVC - asp.net-mvc

I'm facing a problem in mvc 4, i have create action method where i insert the data in db and then return model to View but the model data not bind to hidden Field like Id and Voucher number,
in other fields data binds properly but the issue is with these Id and VoucherNum, Id is primary key and VoucherNum is Unique.
I have mentioned the code and html.
Code:
[HttpPost]
public ActionResult Create(Payment payment)
{
payment.VoucherNum = db.Payments.Count() + 1;
Ledger ledger = new Ledger();
ledger.CusId = payment.CustomerId;
ledger.Date = payment.Date;
ledger.Remarks = payment.Remarks;
ledger.Type = payment.Type;
string negativeAmount;
negativeAmount = "-" + payment.Amount;
ledger.Amount = Convert.ToInt32(negativeAmount);
ledger.IsActive = true;
payment.IsActive = true;
db.Payments.Add(payment);
db.Ledgers.Add(ledger);
db.SaveChanges();
ViewBag.CustomerId = new SelectList(db.Customers.ToList()
.Select(x => new { Id = x.Id, CustomerCode = x.Name + "-" + x.CustomerCode }
), "Id", "CustomerCode", payment.CustomerId);
var model = db.Payments.Find(payment.Id);
return View(model);
}
<h2>Payments</h2>
<hr />
<div class="row">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Vocher#</label>
#Html.TextBoxFor(model => model.VoucherNum, new { #class = "form-control", #readonly="true"})
#Html.HiddenFor(model=>model.Id)
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Customer</label>
#Html.DropDownList("CustomerId", (IEnumerable<SelectListItem>)ViewBag.CusId, "--Select Customer--", new { #class = "form-control" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Date</label>
#Html.TextBoxFor(model => model.Date, new { #class = "form-control", #id = "date"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Amount</label>
#Html.TextBoxFor(model => model.Amount, new { #class = "form-control" })
</div>
<div class="form-group">
<label class="control-label">Type</label>
#Html.TextBoxFor(model => model.Type, new { #class = "form-control" })
</div>
</div>
<div class="col-md-8">
<div class="form-group">
<label class="control-label">Remarks</label>
#Html.TextAreaFor(model => model.Remarks, new { #class = "form-control", #rows = "5" })
</div>
<input type="submit" value="Payment Receive" class="btn btn-primary pull-right" />
</div>
}
</div>

Because the helper method will look in to the model state dictionary first to populate the value of the input. The model state dictionary currently has null value for the VoucherNum before you saved it, and the helper method will use this value to generate the value of the input field.
To fix this, you can explicitly clear the model state dictionary before returning to the view,
db.Payments.Add(payment);
db.SaveChanges();
ModelState.Clear();
var p = db.Payments.Find(model.Id);
return View(p);
Or even better, follow the PRG pattern. With the Post-Redirect-Get pattern, After successfully updating the database, you will return a Redirect Response back to the client and the client will issue a totally new GET request to the server.
In your case, You can use RedirectToAction to return a 302 response.
db.SaveChanges();
return RedirectToAction("Edit",new {id=payment.Id});
This will tell the browser to issue a new GET request for Edit action method with the new id in the request url. Your GET action method will use this id and get the entity from db and return it.
public ActionResult Edit(int id)
{
var model = db.Payments.Find(id);
return View(model);
}
I strongly recommend you using the PRG pattern.

Related

Securely Show and post a Form

suppose you show a form using beginform
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(m => m.IsLock, new { #Class = "col-md-12 control-label" })
<div class="col-md-12">
#using (Html.BeginForm("ChangeLockUser", "UserAdmin", new { area = "Admin", id = Model.UserId }, FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.Hidden("returnUrl", Url.Action("Details", "UserAdmin", new { Area = "Admin", id = Model.UserId }))
<div class="input-group mb-3">
#Html.TextBox("LockStatus", Model.IsLock ? "غیر فعال" : "فعال", new { #class = "col-md-12 form-control", #readonly = "readonly" })
<div class="input-group-append">
<button type="submit" class="btn btn-outline-secondary">#(Model.IsLock ? "فعال شود" : "غیرفعال شود")</button>
</div>
</div>
}
</div>
</div>
before submitting I can easily change the Id in the post address so the form will manipulate another record!!!
is there any method to solve this security problem?
thank you
Don't use UserId in front-end, you can get the current logged in user in the Controller inside ActionResult like
using Microsoft.AspNet.Identity;
string currentUserId = User.Identity.GetUserId();
Get the UserId after submitting then use it.

How to validate a duplicate post request from a form in MVC

I am having an form which insert a record whenever a post request is made,but the problem is that if someone clicks submit button more than 1 times than duplicate post request are made and at the end same records are getting inserted. I dont want to check that the record is already present or not because the record would be different always. I tried using ValidateAntiForgeryToken filter in controller but it is failing to validate the requests, Below is my View Code.
#using (Html.BeginForm("Create", "Home",FormMethod.Post,new { onkeydown = "return event.keyCode!=13" }))
{
#Html.AntiForgeryToken()
<div class="right-col">
#Html.TextBoxFor(model => model.Name, new { placeholder = "Name", #class = "small-box" })
</div>
<div class="left-col">Email Id :</div>
<div class="right-col">
#Html.TextBoxFor(model => model.EmailId, new { placeholder = "Email Id", #class = "small-box",id="resumeemailid" })
#Html.ValidationMessageFor(model => model.EmailId)
</div>
<div class="left-col">Address :</div>
<div class="right-col">
#Html.TextAreaFor(model => model.Address, new { placeholder = "Address", #class = "small-box" })
</div>
<div class="buttons resume-threebutton">
<input type="submit" id="register-button" class="gradient-btn" value="#T("Account.Passport.Register.Button.Upload", "Upload")" name="Command" />
<input type="submit" id="register-button" class="gradient-btn" value="#T("Account.Passport.Button.UploadandAdd", "Upload And Add New")" name="Command" />
<input type="button" id="register-button" class="gradient-btn" value="#T("Account.Passport.Register.Button.Cancel", "cancel")" name="register-button" onclick="location.href='#Url.Action("SelectTemplate", "CustomerTemplate")'" />
</div>
}
and below is my controller Post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ProductModel model)
{
//To do add the product here...
}

ASP.NET MVC - A potentially dangerous Request.Form value was detected from the client

Am trying to click save button to update what I have in text editor using ckeditor but I got this error
A potentially dangerous Request.Form value was detected from the client (OPTION_VALUE="Welcome to the Na...").
The controller is shown below
Controller
public ActionResult EditRegistrationGuideline(long id)
{
OPTIONS options = _optionsService.GetOption(id);
return View(options);
}
//
// POST: /Product/Edit/5
[HttpPost]
public ActionResult EditRegistrationGuideline(long id, OPTIONS options)
{
try
{
// TODO: Add update logic here
if (ModelState.IsValid)
{
options.OPTION_ID = id;
options.ACTION_STATUS = 0;
options.CREATED_DATE = DateTime.Now;
_optionsService.AddOption(options);
return RedirectToAction("Index");
}
}
catch
{
//return View();
ModelState.AddModelError("", "We cannot edit this Option. Verify your data entries !");
}
return View();
}
and the view is here
View
#{
//ViewBag.Title = "CreateRegistrationGuideline";
}
<div class="content-header clearfix">
<h1 class="pull-left">
<i class="fa fa-plus"> </i> Edit Registration Guideline
</h1>
<div class="col-xs-3 pull-right">
<input type="button" class="btn btn-block btn-warning" value="Back" onclick="location.href='#Url.Action("IndexRegistrationGuideline", "Options")'" />
</div>
<div class=" box box-body box-primary">
#using (Html.BeginForm("EditRegistrationGuideline", "Options", FormMethod.Post, new { #class = "form-horizontal", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#*<h4>OPTIONS</h4>
<hr />*#
#*#Html.ValidationSummary(true)*#
#Html.ValidationSummary(false, null, new { #class = "text-danger" })
<div class="row .col">
<div style="margin-top:20px" class="mainbox col-md-12 col-md-offset-0 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Edit Option</div>
</div>
<div class="panel-body">
#*<div class="form-group">
#Html.LabelFor(model => model.OPTION_NAME, new { #class = "control-label col-md-2" })
<div class="col-md-10">*#
#*#Html.LabelFor(model => model.OPTION_NAME, new { #class = "control-label col-md-2" })
<div class="col-md-10">*#
#*#Html.EditorFor(model => model.OPTION_NAME)*#
#*#Html.HiddenFor(model => model.faculty_activation_date, new { #Value = System.DateTime.Now })*#
#Html.HiddenFor(model => model.OPTION_NAME)
#Html.ValidationMessageFor(model => model.OPTION_NAME)
<div class="form-group">
#*#Html.LabelFor(model => model.OPTION_VALUE, new { #class = "control-label col-md-2" })*#
<div class="col-md-10">
#Html.LabelFor(model => model.OPTION_VALUE, "Option Value")
#*<textarea class="form-control" placeholder="Enter Option Value" name="OPTION_VALUE" id="editor1"></textarea>*#
#Html.TextAreaFor(model => model.OPTION_VALUE, new { #class = "form-control", #id = "editor1" })
#Html.ValidationMessageFor(model => model.OPTION_VALUE, "", new { #class = "text-danger" })
</div>
</div>
#*<div>
#Html.LabelFor(model => model.OPTION_VALUE, "Option Value")
#Html.TextAreaFor(model => model.OPTION_VALUE, new { #type = "textarea", #id="editor1", #class = "form-control", #placeholder = "Enter Option Value", #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.OPTION_VALUE, null, new { #class = "text-danger" })
</div>*#
#*<div class="form-group">
#Html.LabelFor(model => model.ACTION_STATUS, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ACTION_STATUS)
#Html.ValidationMessageFor(model => model.ACTION_STATUS)
</div>
</div>*#
</div>
<div class="panel-footer">
<div class="panel-title">
<div class="form-actions no-color">
<input type="submit" value="Save" class="btn btn-success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
#*<div>
#Html.ActionLink("Back to List", "Index")
</div>*#
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/jquery-3.1.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="https://cdn.ckeditor.com/4.5.7/standard/ckeditor.js"></script>
<script>
$(function () {
CKEDITOR.replace('editor1');
});
</script>
}
Please what do I do.
I use CKEDITOR
XSS error ("A potentially dangerous Request.Form value was detected from the client(...)").
Solution:
[ValidateInput(false)]
Differnce b/w them
AllowHtml:
The AllowHtml attribute can be applied to a Model property and it will disable the validation by ASP.Net MVC only for that particular property
Advantages
The AllowHtml attribute is developed for Model class.
The Scope is limited to specific property of the Model class.
It is the safe and recommended solution.
ValidateInput
The ValidateInput attribute can be applied to a Controller’s Action method and it will disable the validation by ASP.Net MVC only for that particular Action method.
Advantages
The Scope is limited to specific Action method of the Controller class.
If you have multiple properties accepting HTML content, then this method will reduce redundancy.
When Model class is not used for designing Form elements then this attribute is needed.For complete details Link
Just place ValidateInput(false) attribute on controller's action.
[HttpPost]
[ValidateInput(false)]
public ActionResult EditRegistrationGuideline(long id, OPTIONS options)
The other option is to place [AllowHtml] attribute on Model Property, have a look on SO post to get difference between them
ValidateInput(false) vs AllowHtml
I was experiencing a similar issue with the following error message A potentially dangerous Request.Form value was detected from the client.
As an alternative to the previous answers, I found encoding the value passed back to the controller worked. CKEditor allows you to do this by setting the config config.htmlEncodeOutput = true;.
The documentation for this can be found at: https://docs-old.ckeditor.com/ckeditor_api/symbols/CKEDITOR.config.html#.htmlEncodeOutput
I got this error while testing XSS on my site.
This is a very good feature that the model gives us that prevents XSS, CSRF from penetrating your site.
Do not disable it as much as possible.

How do I post varied length list to controller?

MVC4. I have a dynamic list on a view, a new textbox is added with button click (which adds a partialView) so user can enter a list of stuff. That is contained in a form element with a submit button.
In the controller I have tried three different types:
[HttpPost]
public ActionResult Index(IEnumerable<AccessoryVM> form)
{
-- form is NULL
[HttpPost]
public ActionResult Index(AccessoryVM form)
{
-- form has only the first item in the list
[HttpPost]
public ActionResult Index(FormCollection form)
{
-- seems to receive the list, but having trouble getting the values
All the examples I have seen are using a for list to add an index to each item, but they aren't using a dynamic list (it has a fixed length).
What should the Controller receiving type be?
EDIT to add more detail:
Button click appends partial view:
$("#add-item").on("click", function () {
$.ajax({
url: '/Accessory/AddItem',
cache: false,
success: function (html) {
$("#form-body").append(html);
}
})
return false;
})
Partial View:
#model EmployeeHardwareRequest.Models.ViewModels.AccessoryVM
<div class="form-group col-md-3">
#Html.LabelFor(model => model.ItemDescription, htmlAttributes: new { #class = "control-label" })
<div>
#Html.DropDownListFor(model => model.AccessoryId, Model.AccessoryDdl, new { #class = "form-control" })
</div>
</div>
<div class="form-group col-md-9">
#Html.LabelFor(model => model.ProductLink, htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.ProductLink, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProductLink, "", new { #class = "text-danger" })
</div>
</div>
Main View - partial is appended to the #form-body:
#using (Html.BeginForm("Index", "Accessory", FormMethod.Post, new { #id="accessory-form", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div id="form-body">
<div class="form-group col-md-3">
#Html.LabelFor(model => model.ItemDescription, htmlAttributes: new { #class = "control-label" })
<div>
#Html.DropDownListFor(model => model.SelectedAccessory, Model.AccessoryDdl, new { #class = "form-control" })
</div>
</div>
<div class="form-group col-md-9">
#Html.LabelFor(model => model.ProductLink, htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.ProductLink, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProductLink, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<button id="add-item" class="btn btn-primary">Add Another Item</button>
</div>
<div class="col-md-6">
<input type="submit" value="Select Software" class="btn btn-default pull-right" />
</div>
</div>
}
Most likely, you have a binding issue. Assuming all you're posting is a collection of AccessoryVM, then the parameter should be List<AccessoryVM> form. However, in order for the modelbinder properly bind the posted values, your input names must be in a specific format, namely either form[N].Foo or just [N].Foo.
You haven't given any detail about what's going on in your view, but since these are dynamically added, you must be using JavaScript to add additional inputs to the page. If you're doing it via AJAX (returning a partial view), you'll need to pass the index to your endpoint so that it can be utilized to generate the right input names. If you're using something like Knockout, Angular, etc., you should ensure that whatever template is being used to generate the inputs takes an index into account.

Retrieving dropdown values from partial view during post method

I need to get the selected dropdown value from partial view and included in the CategoryID in the Book class...
[Authorize]
public PartialViewResult GetAllCategory()
{
ProcessSVC.Category newCategory = new ProcessSVC.Category();
newCategory.ChildCategories = obj1.GetAllCategories(String.Empty);
return PartialView(newCategory);
}
GetAllCategory.cshtml (PartialView)
#model MvcAdminTemplate.ProcessSVC.Category
#Html.DropDownListFor(m => m.ParentID, new SelectList(Model.ChildCategories, "ID", "DisplayName"), new { #class = "form-control" })
Create View:
[Authorize]
[HttpPost]
public ActionResult Create()
{
MvcAdminTemplate.ProcessSVC.Book oBook = new ProcessSVC.Book();
return View(oBook);
}
Create.cshtml
#model MvcAdminTemplate.ProcessSVC.Book
#{
ViewBag.Title = "Create";
}
#Html.Partial("_LeftMenu")
<!-- content -->
<h2>Create</h2>
<div class="col-md-10">
#using (Html.BeginForm("Create", "Books", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="row">
<div class="bootstrap-admin-no-table-panel-content bootstrap-admin-panel-content collapse in">
<form class="form-horizontal">
<fieldset>
<legend>Add Book</legend>
<div class="form-group">
<div class="col-lg-2">
#Html.LabelFor(m => m.BookName, new { #class = "control-label" })
</div>
<div class="col-lg-10">
#Html.TextBoxFor(m => m.BookName, new { #class = "form-control", type = "text" })
<p class="help-block"> </p>
</div>
</div>
<div class="form-group">
<div class="col-lg-2">
#Html.LabelFor(m => m.Description, new { #class = " control-label" })
</div>
<div class="col-lg-10">
#Html.TextBoxFor(m => m.Description, new { #class = "form-control ", type = "text" })
<p class="help-block"> </p>
</div>
</div>
<div class="form-group divCategory">
<div class="col-lg-2">
#Html.Label("Parent Category", new { #class = " control-label" })
</div>
<div class="col-lg-10">
#Html.HiddenFor(m => m.BookId, new { #class = "hdn-id" })
#Html.Action("GetAllCategory","Books")
<span class="help-block"> </span>
</div>
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="reset" class="btn btn-default">Cancel</button>
</div>
</fieldset>
</form>
</div>
</div>
}
After submission of the above form i need to get Book attributes and Category "Selected Category".
[Authorize]
[HttpPost]
public ActionResult Create(ProcessSVC.Book oBook)
{
oBook.LanguageID = 1;
_service.AddBooks(oBook.ActualPrice,oBook.ActualPriceString, oBook.Author, oBook.BookId, oBook.BookName, oBook.CategoryID, oBook.Currency, oBook.CurrentPrice, oBook.CurrentPriceString,
oBook.Description, oBook.DiscountPercentage, oBook.DiscountValue, oBook.LanguageID,
oBook.NativeLanguageName, oBook.Publisher);
TempData.Add("SuccessMessage", " New book " + oBook.BookName + " Added !");
return RedirectToAction("Index");
}
Please suggest me.
To correctly bind your model, you need to create the dropdown in the main view. This line in you partial view
#Html.DropDownListFor(m => m.ParentID, ....
will render a select
<select name="ParentID" ...
but you model is expecting a property named CategoryID
In you Create() method, generate the SelectList and assign to a view model property or to ViewBag and then (instead or #Html.Action("GetAllCategory","Books") use
#Html.DropDownListFor(m => m.CategoryID, ....
use this line in your GetAllCategory.cshtml file
#Html.DropDownList("CategoryID", new SelectList(Model.ChildCategories, "ID", "DisplayName"), new { #class = "form-control" })

Resources