Handling invalid request inside an Action - c#

This is my first application in MVC.
I want to check whether list property named movies, of object model, has a value or not. So that if a user attempts to find details of a movie and that movie doesn't exist he should get an error.
Lets say, movies list only has data for two movies, but user attempts to access: /Movie/Details/3 or /Movie/Details/abc
I want to handle such invalid requests.
MovieController
public class MovieController : Controller
{
MovieCustomerViewModel model = new MovieCustomerViewModel();
// GET: Movie
public ActionResult Index()
{
model.movies = new List<Movie>
{
new Movie{id=1,name="Shrek"},
new Movie{id=1,name="Wall-e"}
};
return View(model);
}
public ActionResult Details(int? id)
{
if (id < 1 || id > 2 || !id.HasValue || model.movies.Count==0)
return HttpNotFound();
else
return View();
}
}
One simple way is to use the Count property of List, but it throws NullReferenceException.
This tells me that model has been re-instantiated and the values assigned in Index are limited to that action only. As a beginner, I don't understand what to do then..
-I can use a constructor for assigning values, but I am not sure about it. Is it a good approach?
-Can this problem be solved with Route attributes?
-Am I missing something about Action attributes or Route attributes at this stage of learning?
Thanks in advance.

Related

Is it possible to have a generic [Bind] in c# methods?

what I try to do is to bind every incomming value from my response to a string or stringlist dynamicly / generic.
So assume I would know each POST-Value of my request, e.g.
string1 = Test
string2 = Test2
I would write:
[HttpPost]
public ActionResult DoFoo(string string1, string string2)
{
}
or
[HttpPost]
public ActionResult DoFoo(string string1, [Bind(Prefix = "string2")string myString2)
{
}
My situation know is, that I have X strings with my post request. So I dont know the exact number nor the names to catch in my backend.
How to catch every given Post-value without knowing this / how to catch the values dynamicly?
I don't feel that why you have to use Prefix with BIND, when you have to bind every incoming field of response. Bind is not a good choice for that. You can use bind if you have multiple entities at the same time. Reference here
that I have X strings with my post request.
If you have to use all the fields then you can use FormCollection or Model object to receive those fields. FormCollection automatically receive all the fields from view and bind them to a collection. See this for proper example. And a code snippet is below for reference.
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
Student student = new Student();
student.FirstName = collection["FirstName"];
student.LastName = collection["LastName"];
DateTime suppliedDate;
DateTime.TryParse(collection["DOB"], out suppliedDate);
student.DOB = suppliedDate;
student.FathersName = collection["FathersName"];
student.MothersName = collection["MothersName"];
studentsList.Add(student);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However if you have to deal with only one particular field/set of fields then you can use either Include or Exclude as per your convenience with BIND. Example shown here and code snipped is added below.
In following way you are telling that you only want to include "FirstName" of User model while receiving the form content. Everything else will be discarded.
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
And in following example you are telling that, please exclude "IsAdmin" field while receiving the fields. In this case, value of IsAdmin will be NULL, irrespective of any data entered/modified by end-user in view. However, in this way, except IsAdmin, data rest of the fields will be available with user object.
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}

Is it possible to RedirectToAction passing data without using parameters in an ASP.NET MVC project?

In my controller of an ASP.NET MVC project, I have a
return RedirectToAction("CreatePerson", "Home")
This View is a form that creates a person and that works fine. However, I want to RedirectToAction and pre-fill the form with data collected from a form that creates a User for the system.
How would I pass the data from the CreateUser form in the CreatePerson form?
I know that I could use parameters, but would this really be the best method if most of the time I am calling the CreatePerson view without needing those parameters.
Any help in the right direction would be greatly appreciated.
You can't send data with a RedirectAction. That's because you're doing a 301 redirection and that goes back to the client.
So better use TempData
Assuming you will have model to createperson with following properties:
public class CreatePersonData
{
public string name {get; set;}
public string address {get; set;}
}
Now fill the model and store in TempData
CreatePersonData person=new CreatePersonData();
person.name="SomeName";
person.address="SomeAddress";
TempData["person"]=person;
return RedirectToAction("CreatePerson", "Home")
Now while receiving just receive it from tempdata and pass the filled model to the view
public ActionResult CreatePerson()
{
CreatePersonData person=new CreatePersonData()
var loadPerson= TempData["person"];
person = loadPerson;
return View(person);
}
UPDATE
As #StephenMuecke made a point of loosing data with TempData you might need to use .Keep or .Peek with TempData to retain the value for future requests
Ex:
with .Peek
//PEEK value so it is not deleted at the end of the request
var loadPerson= TempData.Peek("person");
or with .Keep
//get value marking it from deletion
var loadPerson = TempData["person"];
//later on decide to keep it
TempData.Keep("person");
or as #Stephen said just pass the id and select the user from database
Ex:
return RedirectToAction("CreatePerson", "Home", new { ID = User.ID });
Now in your CreatePerson ActionResult just get it from db as below:
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
return View(person);
}
UPDATE 2
You can combine both of the above approaches like storing data in TempData and passing the ID with routeValues and check if TempData isn't null then fallback to retrieval of data using ID approach.
Ex:
public class CreatePersonData
{
public string Id{get; set;}
public string name {get; set;}
public string address {get; set;}
}
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var loadPerson=(CreatePersonData)TempData.Peek("person"); //cast the object from TempData
if(loadPerson!=null && loadPerson.Id==ID)
{
person=loadPerson;
}
else
{
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
}
return View(person);
}
Many Overloads exist for this
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
What you want to do is right click on that Method and View Definition, you should see many overloads for it
// Summary:
// Redirects to the specified action using the action name and route values.
//
// Parameters:
// actionName:
// The name of the action.
//
// routeValues:
// The parameters for a route.
//
// Returns:
// The redirect result object.
1- create an object of your model
2- you can use session to pass parameter to your other action,
3- then put the object in a viewbag agg get it in your view

The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'IMS.Models.Dealer'

I know there are a lot of similar questions here, but none of them could solve my problem.
When I access the URL: http://localhost:42626/dealer/edit/2
Error occurs:
The model item passed into the dictionary is of type 'System.Int32',
but this dictionary requires a model item of type 'IMS.Models.Dealer'.
DealerController Code:
[HttpGet]
public ActionResult Edit(int DealerId = 0)
{
//get from database
Models.Dealer dealer = new Models.Dealer();
string Error;
if(dealer.GetDealer(DealerId, out Error))
return View(dealer);
else
{
RedirectToAction("Index");
}
return View(DealerId);
}
[HttpPost]
public ActionResult Edit(Models.Dealer dealer)
{
//if All validations are true
if (ModelState.IsValid)
{
string Error;
//save to database
if(dealer.Save(out Error))
return RedirectToAction("Index");
else
{
TempData["EditMessage"] = "An error occured. Could not update Dealer. Details: " + Error;
return Edit(dealer.Id);
}
}
return Edit(dealer.Id);
}
I've created View with strongly typed Models.Dealer and template is Edit.
If I have defined [HttpGet] and [HttpPost], why is it not accepting int and keep asking for Dealer model??
Currently if the dealer ID can't be found, you're calling RedirectToAction, but ignoring the result and then trying to return your view with the dealer ID. I suspect you want:
[HttpGet]
public ActionResult Edit(int dealerId = 0)
{
//get from database
Models.Dealer dealer = new Models.Dealer();
string error;
if (dealer.GetDealer(dealerId, out error))
{
return View(dealer);
}
else
{
return RedirectToAction("Index");
}
}
I've updated the variable names to be more idiomatic, btw. It feels odd that a dealer itself has GetDealer, mind you - I'd expect some kind of DealerService to be provided to your controller via dependency injection, so you'd then have:
Dealer dealer = dealerService.GetDealer(dealerId);
(I'd also probably use exceptions for error handling rather than strings like this, but that's a different matter.)

How to pass List in Redirecttoaction

I want to pass more then one parameter from RedirectToAction method
how can I pass?
My One Action Method
[HttpPost, ActionName("SelectQuestion")]
public ActionResult SelectQuestion(string email,List<QuestionClass.Tabelfields> model)
{
List<QuestionClass.Tabelfields> fadd = new List<QuestionClass.Tabelfields>();
for (int i = 0; i < model.Count; i++)
{
if (model[i].SelectedCheckbox == true)
{
List<QuestionClass.Tabelfields> f = new List<QuestionClass.Tabelfields>();
fadd.Add(model[i]);
}
}
return RedirectToAction("Question", new { email = email, model = fadd.ToList() });
}
My another Action Method
[HttpGet]
public ActionResult Question(string email,List<QuestionClass.Tabelfields> model)
{
}
I am not getting values in model.
You cannot pass a collection of complex objects in urls when redirecting.
One possibility would be to use TempData:
TempData["list"] = fadd.ToList();
return RedirectToAction("Question", new { email = email});
and then inside the Question action:
var model = TempData["list"] as List<QuestionClass.Tablefields>;
The way that I solved this problem was to serialize the list to a JSON object using the JsonConvert method from the Newtonsoft.Json nuget package. Then the serialized list can be passed as a parameter and then deserialized again to re-create the original list.
So in your SelectQuestion method you would use this code:
return RedirectToAction("Question",
new {
email = email,
serializedModel = JsonConvert.SerializeObject(fadd.ToList())
});
And in your Question method, you would use this code to deserialize the object.
[HttpGet]
public ActionResult Question(string email, string serializedModel)
{
// Deserialize your model back to a list again here.
List<QuestionClass.Tabelfields> model = JsonConvert.DeserializeObject<List<QuestionClass.Tabelfields>>(serializedModel);
}
Important, this adds the model as a query string parameter to your url, so only do this with really simple small objects, otherwise your url will be too long.
This is probably not even active anymore, but I'll leave how I did it here to maybe help someone else.
I solved this using a simple Redirect instead of a RedirectToAction:
List<int> myList = myListofItems;
var list = HttpUtility.ParseQueryString("");
myList.ForEach(x => list.Add("parameterList", x.ToString()));
return Redirect("/MyPath?" + list);
Then, on your other method:
public ActionResult Action(List<int> parameterList){}
RedirectToAction method Returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
You should either keep the data in a temporary storage like TempData / Session . TempData uses Session as the backing storage.
If you want to keep it real Stateless, you should pass an id in the query string and Fetch the List of items in your GET Action. Truly Stateless.
return RedirectToAction("Question", new { email = email,id=model.ID });
and in your GET method
public ActionResult Question(string email,int id)
{
List<QuestionClass.Tabelfields> fadd=repositary.GetTabelFieldsFromID(id);
//Do whatever with this
return View();
}
Assuming repositary.GetTabelFieldsFromID returns a List of TabelFields from the Id

Bound object never be null

For instance, there is some object that will be model for strongly-typed view:
class SomeModel {
public string SomeUsualField { get; set; }
}
Further, some action exists in controller that will work with object specified above:
public ActionResult Index(SomeModel obj)
{
return View(obj);
}
So the question is why obj isn't a null while Index action first called? It's created new instance of SomeModel class with null SomeUsualField.
The ASP.NET MVC model binding infrastructure tries to fill all properties with data coming from the request object (query string, form fields, ...). Therefore it creates an instance of all parameters of the controller to try to match the properties.
Because you don't pass a SomeUsualField, it is null, but the parameter object has an empty instance.
You can initialize the property SomeUsualField when you call http://localhost/MySite/MyController/Index?SomeUsualField=test. The property SomeUsualField will automagically initialized with 'test'
If you don't want the parameter to be set, you can leave it away and make a 2nd action with the attribute [HttpPost]. A good tutorial is the music store.
public ActionResult Index()
{
var obj = new SomeModel();
return View(obj);
}
[HttpPost]
public ActionResult Index(SomeModel obj)
{
// update logic
return View(obj);
}

Categories

Resources