I am new to ASP.MVC. My background is in ASP.NET Web Forms, I think this is what is causing my confusion. I understand that the "M" basically represents the data source, the "V" represents the resource I'm requesting and the "C" dictates what gets shown to an end-user. But then I get confused.
For instance, I'm just trying to create a Login screen. I envision a user visiting "http://www.myapp.com/Account/Login" and they will be presented with a traditional Login screen. To accomplish this, I have added the following in the RegisterRoutes method in my Global.asax file:
routes.MapRoute(
"Login",
"{controller}/{action}",
new { controller = "Account", action = "Login", id = "" }
);
The Login action executes, but this is where I get confused. You see, the first time the login screen loads, I would expect to just show a username/password field. Then on post, I would expect the form to be validated and processed. In an attempt to do this, I have created the following method:
public ActionResult Login()
{
bool isFormValid = ValidateForm();
if (isFormValid)
LoginUser();
else
ShowErrors();
return View();
}
My confusion rests with the Login action. Initially, there is no data. But the next time, I want to validate the data. How do I determine if the Action is a postback or not?
Thank you!
The easiest way to handle this is with two actions: one for get, one for post. Use the AcceptVerbs attribute to control which gets invoked depending on the method. BTW, the default routes should work just fine for this case since when the controller and action is supplied it gets directed as you would expect. I thought that this scenario was also covered in the project template -- did you set up a project using the template or an empty one?
[AcceptVerbs( HttpVerbs.Get )]
public ActionResult Login()
{
}
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult Login( string userName, string password )
{
}
You need two different methods, for Post and Get.
[AcceptVerbs (HttpVerbs.Get]
public ActionResult Login ()
{
return View ();
}
[AcceptVerbs (HttpVerbs.Post]
public ActionResult Login (FormCollection form)
{
if (AuthenticationSuccess ())
return RedirectToAction ("Account");
else
return View ();
}
For Post version you can use the model binding mechanism:
public ActionResult Login (LoginModel loginModel)
Or
public ActionResult Login (string LoginName, string Password)
Related
I'm using returnUrl to return to the last requested page when logging in after being logged out due to inactivity. In itself this works fine, unless the user does not requests a page, but some other action. What I would like is that in case the first action after SessionTimeOut is for example "addItem", the returnUrl is set as "controller/index" instead of "controller/addItem".
After SessionTimeOut the application does not automatically redirect to the login page. The redirect happens as soon as the user makes a new request after SessionTimeOut (as requested by client). So when the user clicks a menu item, the returnUrl is set to the requested page. Great so far.
The issue is though when the user click for example the addItem button that the returnUrl is set as the action method linked to that button. I don't understand how I can exclude this kind of methods from returnUrl.
This is a very scaled down example of the application, to give an idea of how it hangs together.
MainController:
public ActionResult MainPage(){
return View();
}
public ActionResult addItem(){
--- Do Something ---
}
public ActionResult SecondPage(){
return View();
}
Login method:
public ActionResult Login([Binde(Include = "UserName, Password")]LoginModel model, string returnUrl)
{
if(Modelstate.IsValid)
{
--- Do Validation ---
return Redirect(returnUrl);
}
}
In case the user is on the MainPage and makes after SessionTimeOut the request for "SecondPage", the returnUrl should be and is "controller/SecondPage".(OK)
In case the suer is on the MainPage and makes after SessionTimeOut the request for "addItem", returnUrl should be "controller/MainPage" and not "controller/addItem"
One quick solution is to put Action Filters into effect.
As you might know, an action filter is an attribute that you can apply to a controller action -- or an entire controller -- that modifies the way in which the action is executed. More details, see documentation here
In this case, you need to override OnActionExecuting method, is called before a controller action is executed, so that prior to execution, you can decide to redirect or not based on the return value.
I provide you with a simple example :
public class myCheckUrlActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<string> avoidList = new List<string> { "addItem", "removeItem"};
foreach(string i in avoidList)
{
if(filterContext.HttpContext.Request.Url.Query.Contains(i))
{
filterContext.Result = new RedirectResult("/Home/MainPage") ;
break;
}
}
}
}
Now, Let's apply this filter to all action methods inside your Home Controller, though you may apply it individually to only certain action methods.
[myCheckUrlActionFilter]
public class HomeController : Controller
{ .... }
Access to HttpContext inside the body of the filter, enables to extract session, and even current controller and action name. Therefore, you can check session state here in addition returnUrl existence to further decide on redirect. So, customize it as you need.
Hope this helps.
I have an MVC5 application which has several controllers, scaffolded with EF6 CRUD actions and associated views. One of these controller/view sets is used for managing a table of patient identifiers, and on completion of an edit or delete, the controller returns an action link to the identifiers index view, as expected.
However, the patient identifiers are also displayed on the various views of the patients controller, and from the Patient.Edit view I have Html.ActionLink calls to the identifier controller's edit or delete actions. When the latter are called from the Patient.Edit view, I would like them to return to that on completion.
Is there any way I can accomplish this?
Yes, but this is always a manual process. There's nothing built into MVC specifically for return URLs.
Essentially, your links to edit/delete will need to include a GET param, usually called returnUrl, though the name doesn't matter, which will be set to the current page URL. For example:
#Html.ActionLink("Edit", new { id = patient.Id, returnUrl = Request.RawUrl })
Then, your edit/delete GET action should accept this parameter, and set a ViewBag member:
public ActionResult Edit(int id, string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
In your edit form, add a hidden field:
#Html.Hidden("returnUrl", ViewBag.ReturnUrl)
In your POST edit action, again, accept the param:
[HttpPost]
public ActionResult Edit(int id, Patient model, string returnUrl = null)
But inside this action is where you'll do something different now. Typically, when you've got a successful post and have saved the object or whatever, you then do something like:
return RedirectToAction("Index");
However, instead, you should now check to see if returnUrl has a value, and if it does, redirect to that instead:
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index");
The MVC5 with Identity sample project has a nice helper method that it uses:
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
That would just go into your controller and basically does the same as I've already described with two notable differences:
It uses Url.IsLocalUrl to check that the return url is actually a URL on this site. That's a smart check, as since this is initially passed in the query string of the URL, it's open to be manipulated by a user.
It encapsulates the logic, so you don't have to remember how to this should be handled. When you have a successful POST, you simply return RedirectToLocal(returnUrl), and if there's a return URL set, it will be used. Otherwise, the fallback redirect will used.
This is how I did it in one of my projects:
public ActionResult Edit(int id, string returnUrl)
{
// find the model (code not shown)
return View(model);
}
In the Edit view you don't need to do anything special, in the Post Action you have
[HttpPost]
public ActionResult Edit(Model model)
{
if (ModelState.IsValid)
{
// save Model...
return Redirect(Request.Params["returnUrl"]);
// Request.Query will work as well since it is in the querystring
// of course you should check and validate it as well...
}
// else return the View as usual, not shown
}
To use it, when creating the "Edit" link from your pages you simply need to specify the extra returnUrl parameter:
#Html.ActionLink("Edit", "Edit",
new { controller = "YourController",
returnUrl = Url.Action("Index", "ThisController",)
})
Hope it helps.
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.
I was trying to figure out whether it would be possible to present two different login screens based on the authorization role. The requirement is simple. I have two roles say "admin" and "public". There are "Authorize" attributes marked all over my applications action methods for these two roles.
Now the requirements of my application specifies different login screens for "admin" and "public". The "admin" login screen is secured by an additional security code which is not required for "public" login screen. What I was looking for is some way to know who is trying to log in based on the Action method invoked. If the action method invoked is decorated by Authorize[Roles="admin"] then I would present the admin login screen, whereas if action method invoked is applied Authorize[Roles="public"] then I need to present the public login screen.
If the Login Screen is directly invoked then by default the public login screen would be presented.
It may sound a little weird but this is the scenario I am trying to figure out the solution for.
You could write a custom authorize attribute which will redirect to the proper logon action:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var roles = Roles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "account",
action = "adminlogon",
returnUrl = filterContext.HttpContext.Request.RawUrl
}));
}
else
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "account",
action = "logon",
returnUrl = filterContext.HttpContext.Request.RawUrl
}));
}
}
}
and then decorate your controllers/actions with it:
[MyAuthorize(Roles = "admin")]
public ActionResult Foo()
{
return View();
}
[MyAuthorize(Roles = "normaluser")]
public ActionResult Bar()
{
return View();
}
Now if a non authenticated user tries to hit the Foo action he will be redirected to the /account/adminlogon action and if he tries to hit the Bar action he would be redirected to the /account/logon action. In both cases the current url will be passed as returnUrl parameter so that upon successful login the user could be brought to the page that he was initially trying to browse.
I have a registration form in the side bar of my web application. When the user submits the entered data, the user should be redirected to another page with a more complete registration form when he can fill the rest of the data. The data that was entered by the user in the first form should be already there in the second form, but that's not happening... I checked to see the value of the view model I'm passing to the second action method and it was null and in the browser's address bar I get:
http://localhost:2732/User/RegisterPage?model=Sharwe.MVC.ViewModels.RegisterPageViewModel
Here's the code:
public ActionResult Register()
{
return PartialView(new RegisterViewModel());
}
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RedirectToAction("RegisterPage", "User", new { viewModel = model });
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}
Isn't that the way to do this? Or am I missing something here...?
The Dictionary passed to RedirectToAction() is the Route Value not the View Model. And RedirectToAction() is basically telling the browser to go to a certain URL. Browser by default makes the GET request and obviously you lose your data.
For this, you need to use TempData dictionary. You can store view model in TempData and then RedirectToAction() to RegisterPage. TempData saves the data for only 1 request span and would delete it automatically. It's ideal for this scenario.
See this for more details > The value for a object inside a viewmodel lost on redirect to action in asp.net mvc 2.0?
In this particular case you don't need to use RedirectToAction, you can simply call the RegisterPage action directly:
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RegisterPage(model);
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}