I am currently doing a project in MVC 3 and can't figure out if a user passes an invalid id (let's say 23233), how can i display a message to the user that item with this id does not exist?
Assuming this is ASP.NET, use Find() in your DbSet to find a user with that Id. If the result is null, use something like RedirectToAction() to send the user to a page explaining the problem.
The VS scaffolding system already does something similar, except it returns an HttpNotFound() instead in the automatically generated code. You can use its logic as a starting point.
first.
You are create a checker method for id.
public bool idChecker(string id)
{
try
{
double numeric = -1;
bool retval = double.TryParse(id, out numeric);
return retval;
}
catch (Exception)
{
return false;
}
}
and you will use idChecker method.
public ActionResult YourActionMethod(string id)
{
if (!idChecker(id))
return Content("Invalid ID"); // or your code
else
return View(); // or your code.
}
Related
I am building a service which requires a somewhat lengthy setup process. I have it broken into 4 models and 4 corresponding views. They are Setup, Setup2, Setup3, and Setup4. Each of these views gathers information from the user which is stored in a User object. I have been passing the user along like this:
[HttpPost]
public ActionResult Setup(FormCollection values)
{
User registeringUser = new User();
registeringUser.email = User.Identity.Name;
registeringUser.fName = values["fName"];
registeringUser.lName = values["lName"];
registeringUser.phone = values["phone"];
return RedirectToAction("/Setup2", registeringUser);
}
For some reason, this seems to work just fine for the first jump (from Setup to Setup2) but after that I'm getting weird behavior, such as User. getting set to null when the User is passed to another View.
In a related, but slightly different issue, I need the last screen (Setup4) to be recursive. This screen adds a course in which the user is enrolled, and if they don't check the "This was my last class" button, it needs to basically clear the form so they can enter another course.
The entire Controller looks like this:
[HttpPost]
public ActionResult Setup4(FormCollection values, User registeringUser)
{
// values["allClassesAdded"] returns "false" as a string if box is unchecked, returns "true,false" if checked.
// Solution: parse string for "true"
if (utils.parseForTrue(values["allClassesAdded"]))
{
// TODO Redirect to "congratulations you're done" page.
database.CreateUserInDB(registeringUser);
return Redirect("/Home");
}
else
{
// Build course and add it to the list in the User
course c = new course(values);
if (Request.IsAuthenticated)
{
//registeringUser.currentCourses.Add(c);
registeringUser.AddCourse(c);
return RedirectToAction("/Setup4", registeringUser); // <---- This doesn't really work right
//return View();
}
else
{
return Redirect("/Account/Login");
}
}
}
This is my first project with MVC, so if you find that I'm doing the entire thing completely incorrectly, feel free to not answer the question I asked and offer the proper solution to this need. I'm moving an existing (pure) C# project to MVC and I'm mainly just stuck on how to work within MVC's interesting structure. I'm very grateful for any help you can give!
Thanks!
You can store user related data in session without passing it between requests
Smth like this
[HttpPost]
public ActionResult Step1(Step1Model model)
{
Session["UserRegistration"] = new UserRegistration
{
FirstName = model.fName,
....
}
....
}
[HttpPost]
public ActionResult Step2(Step2Model model)
{
var userRegistration = Session["UserRegistration"] as UserRegistration;
if (userRegistration == null) { return Redirrect("Step1"); }
userRegistration.SomeField = model.someField;
...
Session["UserRegistration"] = userRegistration;
....
}
This ought to be simple enough, although I find I can only set cookies but not read them back, despite my browser showing me the cookie.
In my HomeController I set the cookie once a user enters a valid string:
[HttpPost]
public ActionResult Index(string fMemberCode)
{
try
{
// TODO: controller-->member table module-->member data gateway-->DB
// Check member num against member table
// Return cookie if exists
// Return error if not
MembersModule membersModule = new MembersModule();
int memberId = membersModule.AuthMember(fMemberCode);
if (memberId > 0)
{
HttpCookie mCookie = new HttpCookie("MMedia");
mCookie.Value = memberId.ToString();
Response.Cookies.Add(mCookie);
}
else { }
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Then later on, in a different context, the LibraryController needs to check the cookie is present:
public LibraryController()
{
// TODO
// Check member cookie present
int id = int.Parse(Request.Cookies["Media"].Value);
if (id > 0)
this.module = new LibraryModule(id);
else throw new Exception("Invalid cookie");
}
However, when stepping through the code in VS2012 when the line of execution in LibraryController reaches:
int id = int.Parse(Request.Cookies["Media"].Value);
The exception is thrown: Object reference not set to an instance of an object
You can't access the Request property in the constructor of your controller. It doesn't exist at that point in the controller life cycle.
Perhaps an action or controller filter might help you.
I am new to ASP.NET MVC and I wonder if the way I handled these cases is the most appropriate.
I have an "ArticleController", which has an action called "Details" (Used the auto-generate edit template).
By default, there is an optional id at the routing table,
and I want to know how to handle the cases when I don't receive any Id or when I receive a wrong id parameter.
In order to fix it I've wrote this (Note the DefaultValue attribute):
public ViewResult Details([DefaultValue(0)]int id)
{
Article article = db.Articles.Find(id);
if (article == null)
{
return View();
}
return View(article);
}
And at the view I've wrote this:
#if (Model == null)
{
<div>Wrong article id was given.</div>
}
else
{
// Handle as a normal case
}
You would have handled these cases differently? If yes, how?
I think the cleanest approach is to set up your routes so that when no ID is present, a user is routed to a different action. That's what the default route does. For example: /Articles/ will invoke ArticleController::Index(), and /Articles/4 will invoke ArticleController::Details(4).
As far as the case goes where an ID is not found, personally, I prefer to return a 404 error:
return new HttpNotFoundResult("This doesn't exist");
You can make your Id nullable like this:
public ViewResult Details(int? id)
If the user provides no id or an incorrect one, the id won't have a value which you can check with id.HasValue. If the id has a value, you can obtain it with id.Value.
I have an action like shown below. In GetAvailableBookList, I get the list and if there is not any available book redirect to a message page. But in action part code continues to execute and gets an exception and I find myself in error page.
I don't want to use return RedirectToAction or something like that because there are a lot of places where we use this redirect logic in our application.
public ActionResult ActionName()
{
List<BookType> bookList = GetAvailableBookList();
// some code
return View("RelatedView");
}
private List<BookType> GetAvailableBookList()
{
....
list = GetList();
if(list.Count == 0)
{
System.Web.HttpContext.Current.Response.Redirect(messagePageUrl, true);
}
else return list;
}
Unfortunately, Response.Redirect() isn't really friendly with ASP.NET MVC. My rule of thumb is if it comes from HttpContext I don't want to touch it in the controller (of course there are many exceptions to that rule) -- especially since it improves testability.
My suggestion is to use RedirectToAction, but since you don't want to repeat code you can do it in such a way that you don't have to repeat code (although in this case I don't see a problem with repeating code).
public ActionResult LoadBookListAndContinue(
Func<List<BookType>, ActionResult> continuation)
{
var list = LoadBooklist();
if(list.Any())
{
return action(continuation);
}
return new RedirectResult(messagePageUrl);
}
// in your controller
public ActionResult ActionName()
{
return LoadBookListAndContinue(
list => {
// some code
return View("RelatedView");
});
}
Is it pretty? No, but it works better than the Redirect exception.
Use
return RedirectToAction("NoListAvailable");
if you have a specific action you would like to execute. The NoListAvailable action can return a view indicating the problem.
Alternatively, you could return the view directly
return View("NoListAvailable");
The exception you are getting is probably ThreadAbortException and this is something you cannot avoid unless you allow the thread to continue (2nd argument in Response.Redirect).
On a side note your current solution is generally flawed. You should use RedirectToAction in each action when your method returns an empty list.
Throwing a specific exception and redirect where you catch it may be solution
Try to write
System.Web.HttpContext.Current.Response.Redirect(messagePageUrl, false);
General question as I start implementing ASP.NET MVC, I found myself asking, to avoid boxing and unboxing through the framework (check out the signature for "View"), why wouldn't they have simply used generic methods for actions? Maybe they "should have" and they didn't but maybe someone knows of a good reason.
Thanks in advance!
Sorry, an example would be like so...
Edit(int id)
{
...
if(...)
View<Contact>("Edit");
else
View<ShoppingCart>("Cart");
}
EDIT
UPDATED example to reflect my question more accurately
So I can do this:
public ActionResult Customer(int id )
{
try
{
var customer = DB.GetCustomer(id);
if( customer == null )
{
return RedirectToAction("NoCustomerFound");
}
if( customer.IsApproved )
{
return View( TransformToApproved("Approved", customer);
}
return View( "Unapproved", TransformToUnapproved(customer));
}
catch(Exception e )
{
return View("Error", e );
}
}
Update:
Your updated code would just be syntactic sugar. The Model will still get box'd and unboxed when the MVC pipeline starts executing the action and rendering the view.
Even so if you wrote something like this, I'm assuming you'd actually want to pass a model along someplace. Your example doesn't include it.
public ActionResult View<MODEL>(string view, MODEL viewModel )
{
return View(view, viewModel );
}
The generic parameter wouldn't even matter so you'd end up with the same looking calls:
return View("Edit", contact );