How do I handle invalid asp MVC routes - asp.net-mvc

I have a number of MVC views that only make sense in the context of a workflow. E.g. user submits information on another page and is then redirected to a page which only display "success"..
Since the redirect is a GET, there is no way to stop users manually navigating to the page by adjusting their URL.
What should I do in this situation? Redirect to an error page, show a blank page?

In my opinion sucess information views should not have routes, as they are not intended to be served from a browser url. in our projects we usually return the success view directly from an post action.for example,
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
bool changePasswordSucceeded;
try
{
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, userIsOnline: true);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
var infoModel = new AnyInfoModel();
return View("ChangePasswordSuccess", infoModel); // This is not redirected, but success shown in the current route url.
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
return View(model);
}
But this has a drawback also, if an user accidentally hit he F5 button the browser, it will try to repost the form.

Related

How to show error message to user in MVC calling Web API?

I am working on a MVC Web App which is calling a Web API. In my Create (POST) method, a user will enter email addresses of some users in the database. I have a check to enter the email only if the email does not already exist in the database or not. If it already exists, I want to be able to show an error message to the user "Email already exists".
I thought HttpStatusCode.NotFound, "Email exists already." would work, but I think it will only show in PostMan. How can I show it on the MVC side with like ViewBag.ErrorMessage?
API
public IHttpActionResult PostApprovedUsers(ApprovedUsers approvedUsers)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!db.ApprovedUsers.Any(u => u.Email == approvedUsers.Email))
{
db.ApprovedUsers.Add(approvedUsers);
db.SaveChanges();
}
else
{
return Content(HttpStatusCode.NotFound, "Email exists already.");
}
return CreatedAtRoute("DefaultApi", new { id = approvedUsers.Email }, approvedUsers);
MVC Create
public ActionResult Create([Bind(Include = "Email,FirstName,LastName")] ApprovedUsers approvedUsers)
{
using (WebClient client = new WebClient())
{
token = Session.GetDataFromSession<string>("access_token");
client.Headers.Add("authorization", "Bearer " + token);
client.UploadValues(apiUrl, "POST", new NameValueCollection()
{
{ "Email", approvedUsers.Email },
{ "FirstName",approvedUsers.FirstName },
{ "LastName",approvedUsers.LastName }
});
}
return RedirectToAction("Index");
}
The main problem with what you were seeing is that you were not in fact doing anything with the return value of webclient. That is ok though, since based on our discussion everything is in the same project and that would not be how you would want to do this using ASP.NET MVC and Web API.
Since these are all in the same project, we do want to combine common functionality but not within a controller. The DB service should be abstracted away into a seperate project - or class - and then injected into both controllers using some form of IoC Container. There are a couple of options available and I personally usually use Ninject for my .NET projects.
Assuming you have that functionality your MVC Controller and API controller should then both program against that abstraction. Interface based programming is how .NET works best.
With that in mind your new API controller would look something like this:
public IHttpActionResult PostApprovedUsers([Bind(Include="Email, FirstName, LastName")]ApprovedUser approvedUser)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (_db.UserExists(approvedUser))
{
return Content(HttpStatusCode.NotFound, "Email exists Already");
}
_db.AddUser(approvedUser);
return CreatedAtRoute("DefaultApi", new {id = approvedUser.Email}, approvedUser);
}
Where _db is the service that gets injected into the controller. Please not how common functionality such as checking to see if a user exists and adding a user are wrapped behind the abstraction:
public interface IMyFakeDbService
{
IQueryable<ApprovedUser> ApprovedUsers { get; }
int RemoveUser(ApprovedUser user);
int RemoveUser(string email);
bool UserExists(ApprovedUser user);
void AddUser(ApprovedUser user);
}
This then brings us to your MVC Controller. The same principle applies; since all the common functionality is in the db service just inject that into the controller instead of calling the API controller. You will see that you can use ModelState.AddModelError here to pass information back into the view.
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create([Bind(Include = "Email, FirstName, LastName")]ApprovedUser user)
{
if (ModelState.IsValid)
{
if (!_db.UserExists(user))
{
_db.AddUser(user);
return RedirectToAction("Index");
}
ModelState.AddModelError("","A user with that email already exists");
}
return View();
}
Finally
I made a public git repo with a complete project here
The DB calls should be async. You are wasting server resources if not
If you want to learn MVC real quick I would suggest Pro ASP.NET MVC 5 and Pro ASP.NET Web API2
I always use Ok<T>, where T is a model I defined which have 3 variables, one will identify the status (true, false) and one will be a string message, and one will have data type of List<object> which I need to receive at end point, then you can modify the error message as you want
in your case instead of this
return BadRequest(ModelState);
I will replace it with something like this
return Ok<MyResponse>(new MyResponse{ Message = "Error Message Here", Status = false});
then once you get your content you can desirialize it into your object
MyResponse response = new MyResponse();
HttpResponseMessage response = await client.GetAsync(path);
// ok will return success status code, but you will depend on your status to know if it was success or error happened
if (response.IsSuccessStatusCode)
{
// Desirialize your result
response = await response.Content.ReadAsAsync<MyResponse>();
}
// then get the message,
if(response.Status)
{
//it was fine, return something ..
}
else {
// Error happened, get your error message and disaply it, if you want as viewbag
ViewBag.ErrorMessage = response.Message;
// do something
}

Keeping data between sessions without using get, post or cookies

I have one controller class with two action functions. One action is my login page with one view, and the other is my backend page with multiple views.
public ActionResult Login(...)
{
if (logged in or login success)
{
return RedirectToAction("Backend","Controller");
}
...
return View();
}
public ActionResult Backend(...)
{
if(session expired or not logged in)
{
return RedirectToAction("Login","Controller");
}
...
return View("someView");
}
The issue is when the backend action has to send the user to the login action and I want to show the user a message like "Session expired" on the login page.
As an example ViewBag only lives in the current session. But is there a similar and easy way to store information between sessions? So I can set a message in the backend, then redirect to login, and have the login read that message and display it in the view? Kinda like PersistentViewBag.
I really do not want to use get, post or cookies, as is a viable option but then I rather just have the login as its own view in the backend action instead.
You can simply use the querystring for passing data when you are redirecting to the login page.
public ActionResult Backend(...)
{
if(session expired or not logged in)
{
return RedirectToAction("Login","Controller",new { IsSessionExpired = true });
}
...
return View("someView");
}
In your Login action you can check the querystring and decide if you want to display the message.
Update
You can also use TempData if you do not want to use the querystring.
public ActionResult Backend(...)
{
if(session expired or not logged in)
{
TempData["IsSessionExpired"] = true;
return RedirectToAction("Login","Controller");
}
...
return View("someView");
}
Then you can check it in Login action:
if(TempData["IsSessionExpired"] != null)
{
//Show message
}

Authenticating User via Ajax: User.Identity.IsAuthenticated in ajax method, but False elsewhere

I'm using Facebook to preautheticate users, so when they visit a page, an ajax function is called to login the user, and the server will also check to see if the user is already authenticated to the site. These users have entries in the user database on the server.
The server side code is below, which is called by ajax. After that, is a second method I use to get more information on the user that is stored in the database.
When I call User.Identity.IsAuthticaed in the second method, in the same Controller, the User object is still null. The User object contains all the information in FBReg below.
Edit:
After further troubleshooting I found that the ActionResult that calls getUserInfo() has the User object populated. So I'm not sure why getUserInfo() has a null User. I guess I can just pass the object then, but I'm still just curious why this happens.
[HttpPost]
public String FBReg(FBInfo userinfo)
{
..
..
..
if (!User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(userinfo.id, "FBPassword"))
{
FormsAuthentication.SetAuthCookie(userinfo.id, true);
var result = (from u in db.users where (u.username == userinfo.id) select u).FirstOrDefault();
result.LastLoginDate = DateTime.Now;
db.SaveChanges();
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
..
..
..
return "";
}
public UserRepository getUserInfo()
{
bool isauth = false;
try
{
if (User.Identity.IsAuthenticated) // User is always null even after FBReg has User as Authnticated with all the correct information
{
isauth = User.Identity.IsAuthenticated;
}
}
catch { }
// get user info from database to display on page
..
..
..
return userInfo;
}

Why does the previous page link appears in my address bar in MVC3 Razor application?

I am using MVC3/Razor. But I don't understand why the previous page url appears when i redirecToAction.
Scenario: Once the user clicks on Signup from home page like "Account/Signup", the he would get RedirectToAction("AcceptCondition") so once the use accepts the condition and post then the user would get RedirectToAction("Signup") where i will check if he is coming from "AcceptCondition" page then he can proceed otherwise user will go back to "AcceptCondition" page. So when I redirect user from Signup page to AcceptCondition page the url in the address bar appears like "http://localhost:55104/Account/Signup" instead of "http://localhost:55104/Account/AcceptCondition"
I know that i can send user directly to "Account/AcceptCondition" when he click on the signup but I just followed upper scenario.
public ActionResult Signup(string ru)
{
if ((ru == "/Account/AcceptCondition") || (ru == "/Account/Signup"))
{
return View();
}
else
{
return RedirectToAction("AcceptCondition");
}
}
[HttpGet]
public ActionResult AcceptCondition()
{
return View();
}
[HttpPost]
public ActionResult AcceptCondition(AcceptConditionViewModel acceptCondiViewModel)
{
if (acceptCondiViewModel.TermAndCondi == true)
{
return RedirectToAction("Signup",
"Account",
new { ru = "/Account/AcceptCondition" });
}
else
{
return View();
}
}
Signup GET checks refers, if not AcceptCondition, then redirects to AcceptCondition
IF it is AcceptCondition, then render the normal signup view.
AcceptCondition GET renders the AcceptCondition view
AcceptCondition POST does RedirectToAction("SIGNUP")
Signup POST creates the user (Assuming) then goes?
Sounds like the referrer check isn't right so /account/signup is rendered again?
Whats your code look like.
It appears that you are passing a parameter of 'returnUrl' in your first function, but reference it as 'ru' in both the function body and in your RedrectToAction call in the third function. Be sure you are staying consistent with your parameter names as this could be a cause of faulty logic and bad/not working redirects.

MVC 3 -ASP.NET Redirect to Different View

In MVC 3-ASP.NET, I am validating the permission level on the page in the controller. If the user is authorised to see the page then I am using the following code to render it but I don't how to redirect to a new view if not authorised
Could any one tell me how to display alert saying, you are not authorised to see the page and redirect to home page?
public ActionResult viewName()
if(userAuthorised)
{
return View()
}
else
{
//Alert Message
//Redirect to different view like Home Page..
}
Any examples please?
Thank you
You have 2 choices.
1) Create a standard error view and return this in the else:
else
{
ErrorModel viewModel = new ErrorModel(){Msg="Error"});
return View("Error", viewModel);
}
2) Use a Redirect to Action which points at another Controller method which returns the Error View
else
{
return RedirectToAction("BadUser");
}
public ViewResult BadUser()
{
ErrorModel viewModel = new ErrorModel(){Msg="Error"});
return View("Error", viewModel);
}
See Controller.RedirectToAction Method
http://msdn.microsoft.com/ja-jp/library/system.web.mvc.controller.redirecttoaction.aspx

Resources