MVC 3 RTM. Have a model that has an attribute with AllowHtml. In my controller action, if the action has FormCollection as parameter, it throws the exception:
[HttpPost]
public ActionResult Edit(FormCollection collection, int id)
{
var myEntity = _myRepo.Get(id);
TryUpdateModel(myEntity);
return DoSave(myEntity);
}
A potentially dangerous Request.Form
value was detected from the client
However if my controller action uses an object instead of FormCollection it doesn't throw the exception.
[HttpPost]
public ActionResult Edit(MyEntity postedEntity, int id)
{
var myEntity = _myRepo.Get(id);
TryUpdateModel(myEntity);
return DoSave(myEntity);
}
I've already setup
httpRuntime
requestValidationMode="2.0"
Why does it fail when using FormCollection?
You can't use AllowHtml with FormCollection. You could use the [ValidateInput] attribute but obviously this disabled validation for all values:
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(FormCollection collection, int id)
{
var myEntity = _myRepo.Get(id);
TryUpdateModel(objective);
return DoSave(objective);
}
This being said I would use the following:
[HttpPost]
public ActionResult Edit(MyEntity entity)
{
if (ModelState.IsValid)
{
_myRepo.Save(entity);
return RedirectToAction("Success");
}
return View(entity);
}
For security-reasons, simply disabling validation is not a good solution, as you're inadvertently disabling security for that action-method entirely.
When you need just one GET or POST value, this is extremely annoying - for example, Request.Params["xyz"] will throw if there's an HTML-value anywhere in your GET/POST data, even if the "xyz" value you posted does not contain HTML.
(This is true as of the latest MVC 3.1 release.)
To get around this issue, I extended my Controller base-class with the following method:
/// <summary>
/// Gets a value from the current Controller's ValueProvider, bypassing post-data validation.
/// </summary>
public string GetUnvalidatedValue(string key)
{
ValueProviderResult result;
if (ValueProvider is IUnvalidatedValueProvider)
{
result = ((IUnvalidatedValueProvider)ValueProvider)
.GetValue(key, skipValidation: true);
}
else
{
result = ValueProvider.GetValue(key);
}
return result == null ? null : result.AttemptedValue;
}
This effectively allows you to get an individual GET/POST value while bypassing the validation.
I believe this is better, safer and more correct than turning off validation altogether - your application does benefit from the added security, even if the way it's implemented gets in the way, and apparently is pretty painful to get around.
(I don't think this is by design, or at least not by very good design...)
Related
Could any body explain, when to use
TempData
ViewBag
ViewData
I have a requirement, where I need to set a value in a controller one, that controller will redirect to Controller Two and Controller Two will render the View.
I have tried to use ViewBag, the value gets lost by the time I reach Controller Two.
Can I know when to use and advantages or disadvantages?
Thanks
1)TempData
Allows you to store data that will survive for a redirect. Internally it uses the Session as backing store, after the redirect is made the data is automatically evicted. The pattern is the following:
public ActionResult Foo()
{
// store something into the tempdata that will be available during a single redirect
TempData["foo"] = "bar";
// you should always redirect if you store something into TempData to
// a controller action that will consume this data
return RedirectToAction("bar");
}
public ActionResult Bar()
{
var foo = TempData["foo"];
...
}
2)ViewBag, ViewData
Allows you to store data in a controller action that will be used in the corresponding view. This assumes that the action returns a view and doesn't redirect. Lives only during the current request.
The pattern is the following:
public ActionResult Foo()
{
ViewBag.Foo = "bar";
return View();
}
and in the view:
#ViewBag.Foo
or with ViewData:
public ActionResult Foo()
{
ViewData["Foo"] = "bar";
return View();
}
and in the view:
#ViewData["Foo"]
ViewBag is just a dynamic wrapper around ViewData and exists only in ASP.NET MVC 3.
This being said, none of those two constructs should ever be used. You should use view models and strongly typed views. So the correct pattern is the following:
View model:
public class MyViewModel
{
public string Foo { get; set; }
}
Action:
public Action Foo()
{
var model = new MyViewModel { Foo = "bar" };
return View(model);
}
Strongly typed view:
#model MyViewModel
#Model.Foo
After this brief introduction let's answer your question:
My requirement is I want to set a value in a controller one, that
controller will redirect to ControllerTwo and Controller2 will render
the View.
public class OneController: Controller
{
public ActionResult Index()
{
TempData["foo"] = "bar";
return RedirectToAction("index", "two");
}
}
public class TwoController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Foo = TempData["foo"] as string
};
return View(model);
}
}
and the corresponding view (~/Views/Two/Index.cshtml):
#model MyViewModel
#Html.DisplayFor(x => x.Foo)
There are drawbacks of using TempData as well: if the user hits F5 on the target page the data will be lost.
Personally I don't use TempData neither. It's because internally it uses Session and I disable session in my applications. I prefer a more RESTful way to achieve this. Which is: in the first controller action that performs the redirect store the object in your data store and user the generated unique id when redirecting. Then on the target action use this id to fetch back the initially stored object:
public class OneController: Controller
{
public ActionResult Index()
{
var id = Repository.SaveData("foo");
return RedirectToAction("index", "two", new { id = id });
}
}
public class TwoController: Controller
{
public ActionResult Index(string id)
{
var model = new MyViewModel
{
Foo = Repository.GetData(id)
};
return View(model);
}
}
The view stays the same.
TempData
Basically it's like a DataReader, once read, data will be lost.
Check this Video
Example
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
String str = TempData["T"]; //Output - T
return View();
}
}
If you pay attention to the above code, RedirectToAction has no impact over the TempData until TempData is read. So, once TempData is read, values will be lost.
How can i keep the TempData after reading?
Check the output in Action Method Test 1 and Test 2
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["T"] = "T";
return RedirectToAction("About");
}
public ActionResult About()
{
return RedirectToAction("Test1");
}
public ActionResult Test1()
{
string Str = Convert.ToString(TempData["T"]);
TempData.Keep(); // Keep TempData
return RedirectToAction("Test2");
}
public ActionResult Test2()
{
string Str = Convert.ToString(TempData["T"]); //OutPut - T
return View();
}
}
If you pay attention to the above code, data is not lost after RedirectToAction as well as after Reading the Data and the reason is, We are using TempData.Keep(). is that
In this way you can make it persist as long as you wish in other controllers also.
ViewBag/ViewData
The Data will persist to the corresponding View
ViewBag, ViewData, TempData and View State in MVC
http://royalarun.blogspot.in/2013/08/viewbag-viewdata-tempdata-and-view.html
ASP.NET MVC offers us three options ViewData, VieBag and TempData for passing data from controller to view and in next request. ViewData and ViewBag are almost similar and TempData performs additional responsibility.
Similarities between ViewBag & ViewData :
Helps to maintain data when you move from controller to view. Used to
pass data from controller to corresponding view. Short life means
value becomes null when redirection occurs. This is because their goal
is to provide a way to communicate between controllers and views. It’s
a communication mechanism within the server call.
Difference between ViewBag & ViewData:
ViewData is a dictionary of objects that is derived from
ViewDataDictionary class and accessible using strings as keys. ViewBag
is a dynamic property that takes advantage of the new dynamic features
in C# 4.0. ViewData requires typecasting for complex data type and
check for null values to avoid error. ViewBag doesn’t require
typecasting for complex data type.
ViewBag & ViewData Example:
public ActionResult Index()
{
ViewBag.Name = "Arun Prakash";
return View();
}
public ActionResult Index()
{
ViewData["Name"] = "Arun Prakash";
return View();
}
In View, we call like below:
#ViewBag.Name
#ViewData["Name"]
TempData:
Helps to maintain data when you move from one controller to other
controller or from one action to other action. In other words when you
redirect, “Tempdata” helps to maintain data between those redirects.
It internally uses session variables. TempData is meant to be a very
short-lived instance, and you should only use it during the current
and the subsequent requests only
The only scenario where using TempData will reliably work is when you are redirecting. This is because a redirect kills the current request (and sends HTTP status code 302 Object Moved to the client), then creates a new request on the server to serve the redirected view.
It requires typecasting for complex data type and check for null values to avoid error.
public ActionResult Index()
{
var model = new Review()
{
Body = "Start",
Rating=5
};
TempData["ModelName"] = model;
return RedirectToAction("About");
}
public ActionResult About()
{
var model= TempData["ModelName"];
return View(model);
}
void Keep()
Calling this method with in the current action ensures that all the items in TempData are not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep(); // retains all strings values
}
void Keep(string key)
Calling this method with in the current action ensures that specific item in TempData is not removed at the end of the current request.
#model MyProject.Models.EmpModel;
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "About";
var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
TempData.Keep("emp"); // retains only "emp" string values
}
TempData
will be always available until first read, once you read it its not available any more can be useful to pass quick message also to view that will be gone after first read.
ViewBag
Its more useful when passing quickly piece of data to the view, normally you should pass all data to the view through model , but there is cases when you model coming direct from class that is map into database like entity framework
in that case you don't what to change you model to pass a new piece of data, you can stick that into the viewbag
ViewData is just indexed version of ViewBag and was used before MVC3
Also the scope is different between viewbag and temptdata. viewbag is based on first view (not shared between action methods) but temptdata can be shared between an action method and just one another.
in Asp.Net MVC if I decorate an action method with attribute NonAction then it wont be allowed to be called by the user visiting the site.
same happens when I make it private
So whats the difference between the two and is there a special purpose for which NonAction attribute has been made?
For example whats the difference between
[NonAction]
public ActionResult SomeAction(){}
And
private ActionResult SomeAction(){}
in the context of asp.net MVC of course I know one is public and the other one is private
That's the only difference. The attribute is used when you want a method that has a signature that would make it an action, but that you don't want to be an action.
An example for a use for that is a method that action methods call to produce the ActionResult for them:
[NonAction]
public JsonResult JsonInfo(string id, string value) {
return Json(new { id = id, value = value });
}
public JsonResult GetBusInfo() {
return JsonInfo("4", "Bus");
}
public JsonResult GetCarInfo() {
return JsonInfo("8", "Car");
}
The reason to make it public instead of private would be so that actions in other controllers could also use it.
Both works same with action method,you can use them seperately or together.
[NonAction]
private ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FEED_TBL fEED_TBL = db.FEED_TBL.Find(id);
if (fEED_TBL == null)
{
return HttpNotFound();
}
return View(fEED_TBL);
}
If declare it like the above code then when we will try to go to details action method it will not go to it.Rather it will show the error.
{{ HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.}}
This shows that our detail link on view does found any reference to details action method and our controller to.
Image the following controller method:
public ActionResult ShipmentDetails(Order order)
{
return View(new OrderViewModel { Order = order });
}
The incoming order parameter is filled from a custom model binder, that either creates a new order for this session and stores it in the session, or reuses an existing order from the current session. This order instace is now used to fill a shipment details form, where users can enter their address and so on.
When using #using(Html.BeginForm()) in the view. I cannot use the same signature for the post method again (because this would result in ambigious method names) and I found me adding a dummy parameter just to make this work.
[HttpPost]
public ActionResult ShipmentDetails(Order order, object dummy)
{
if (!ModelState.IsValid)
return RedirectToAction("ShipmentDetails");
return RedirectToAction("Initialize", order.PaymentProcessorTyped + "Checkout");
}
What are the best practices for this? Would you simply rename the method to something like PostShipmentDetails() and use one of the overloads of BeginForm? Or does the problem originate from the point, that the first method has the order parameter?
You could use the ActionName attribuite:
[HttpPost]
[ActionName("ShipmentDetails")]
public ActionResult UpdateShipmentDetails(Order order) { ... }
or a more classic pattern:
public ActionResult ShipmentDetails(int orderId)
{
var order = Repository.GetOrder(orderId);
return View(new OrderViewModel { Order = order });
}
[HttpPost]
public ActionResult ShipmentDetails(Order order)
{
if (!ModelState.IsValid)
return RedirectToAction("ShipmentDetails");
return RedirectToAction("Initialize", order.PaymentProcessorTyped + "Checkout");
}
I'm passing in some values to my controller action and everything is binding up fine. There will be two properties missing from the form POST by design.
I am then setting the missing values but then I want to validate the model and it is still saying false since it looks like the ModelState hasn't caught up with my changes.
[HttpPost, Authorize]
public ActionResult Thread(int id, string groupSlug, Comment comment, string submitButton)
{
comment.UserID = UserService.UID;
comment.IP = Request.UserHostAddress;
UpdateModel(comment); //throws invalidoperationexception
if (ModelState.IsValid) // returns false if i skip last line
{
//save and stuff
//redirect
}
//return view
}
What is the cleanest way to pat the ModelState on the head and tell it that everything will be okay whilst still validating everything else that was bound from the user's POST
If the missing Values are required for your model but will not be provided until after binding you may need to clear the errors caused by those two values from the ModelState.
[HttpPost, Authorize]
public ActionResult Thread(int id, string groupSlug, Comment comment, string submitButton)
{
comment.UserID = UserService.UID;
comment.IP = Request.UserHostAddress;
//add these two lines
ModelState["comment.UserID"].Errors.Clear();
ModelState["comment.IP"].Errors.Clear();
UpdateModel(comment); //throws invalidoperationexception
if (ModelState.IsValid) // returns false if i skip last line
{
//save and stuff
//redirect
}
//return view
}
I'm using the ASP.NET Core 1.0.0 and async binding and for me the solution was to use ModelState.Remove and pass the property name (without object name).
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Submit([Bind("AerodromeID,ObservationTimestamp,RawObservation")] WeatherObservation weatherObservation)
{
weatherObservation.SubmitterID = this.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
weatherObservation.RecordTimestamp = DateTime.Now;
ModelState.Remove("SubmitterID");
if (ModelState.IsValid)
{
_context.Add(weatherObservation);
await _context.SaveChangesAsync();
return RedirectToAction("Index", "Aerodrome");
}
return View(weatherObservation);
}
Before .NET Core you can use Validate(TEntity entity) function of controller. But first you have to clear existing ModelState errors.
Suppose you set a missing required property to entity.
ModelState.Clear();
Validate(entity);
if (!ModelState.IsValid) {}
With Core you can use TryValidateModel instead of ModelState.IsValid
see thiis: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-5.0
I'm simply trying to pass the ModelState from one action to another in the same controller, for validation purposes. However, the model state does not get updated. I see that TempData["____MvcContrib_ValidationFailures____"] contains the ModelStateDictionary from the forwarding Action, but I assumed this should get transfered into my current ModelState automatically? Where am I going wrong?
I'm using ASP.NET MVC2 and MVCContrib 2.0.36.0. I have also tried decorating the Controller with this attribute, but the results are the same.
Code:
[HttpGet]
[ModelStateToTempData]
public ActionResult NewsEventsSignup()
{
var newsEventsSignupDetails = this.TempData.GetItem<NewsEventsSignupDetails>();
var viewModel = _newsEventsSignupPageViewModelMapper.MapFrom(newsEventsSignupDetails);
return this.View(viewModel);
}
[HttpPost]
[ModelStateToTempData]
[ValidateAntiForgeryToken]
public ActionResult NewsEventsSignup(NewsEventsSignupFormViewModel newsEventsSignup)
{
ActionResult resultToReturn;
var newsEventsSignupDetails = _newsEventsSignupDetailsMapper.MapFrom(newsEventsSignup);
try
{
_newsEventsSignupTasks.SignupForNewsAndEvents(newsEventsSignupDetails);
resultToReturn = this.RedirectToAction(x => x.Index());
}
catch (RulesException e)
{
e.AddModelStateErrors(this.ModelState); // from xVal
this.TempData.AddItem(newsEventsSignupDetails); // for showing invalid input
resultToReturn = this.RedirectToAction(x => x.NewsEventsSignup());
}
return resultToReturn;
}
How do you check that ModelState is not filled? This is an OnActionExecuted filter, so it is only filled, when the Action finished. You can not check the value in the action.
The easiest way to validate that an ModelState realy has an error is to put a validation summary on the view.
To see that your error is not xval related I would try
ModelState.AddModelError("TestError", "This is an errortest");
In NewsEventsSignup before redirecting.
Also dont try to acces TempData in the debugger or in some debug-code. It is deleted the first time you access it.