Catching and resolving AmbiguousMatchException - asp.net-mvc-3

I would like to catch the AmbiguousMatchException whenever it is thrown and then write some code to resolve it. For example, I have an action ChangePassword which should be called only if the user is logged in. I have another method RenewPassword which must be called if the user is not logged in. I have given the same action name to both these methods.
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}
I want to use the same action name because I do not want the view to have to worry about which action to call. I know that I can write a custom FilterAttribute which will do the reverse of what AuthorizeAttribute does, apply it to the RenewPassword method, and thereby resolve the ambiguity. However, this seems like too much work for a very simple need.
Any better ideas? Is there a built in way to say that a particular action should be executed only for anonymous users and not for logged in users?

If you don't views having to worry about which action to call why not writing a reusable HTML helper:
public static class HtmlExtensions
{
public static MvcForm BeginChangePasswordForm(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated)
{
return htmlHelper.BeginForm("ChangePassword", "SomeController");
}
return htmlHelper.BeginForm("RenewPassword", "SomeController");
}
}
and inside your view:
#using (Html.BeginChangePasswordForm())
{
...
}
and in the corresponding controller:
[HttpPost]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}

Related

ASP.NET MVC 3 - Passing variable model to controller

I have this controller
public class DownloadController : Controller
{
[HttpPost]
public FileContentResult GetFile(MyModel model)
{
// Action code
}
}
And this model
public class MyModel
{
public string Test { get; set; }
}
Passing model from the View to the controller works fine like this
#using (Html.BeginForm("GetFile", "Download", FormMethod.Post))
{
#Html.HiddenFor(m => m.Test)
<button type="submit" name="submit" class="submit">Download</button>
}
The model is correctly passed to the controller and I can do what I need to do with it.
Now, what I'm trying to achieve is to make this GetFile() controller action to be generic, so I can pass it any model, without strongly typing the model class in method signature, like I did in the example above.
I know I can achieve this by overriding GetFile() method once for each model that I have, but I'm wondering is there a better way to do this, to stay DRY as much as possible?
Thank you.
I'd suggest using a base class:
public class BaseGetFileModel {}
which various models will derive from.
[HttpPost]
public FileContentResult GetFile(BaseGetFileModel model)
EDIT:
OK, if you want a generic way of doing this, then you could do this:
[HttpPost]
public FileContentResult GetFile()
{
var someValue = Request["SomeValue"];
}
You don't accept any model parameter, you simply pick up POST'd values from the request. Or you could iterate through the request values collection, if you want to avoid hard-coding key names.

ASP.Net MVC3 Controller Authorize attribute issue

I have a Home Controller with these actions:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
public ActionResult Logoff()
{
HttpContext.Session.Remove("LogonTicket");
HttpContext.Session.Remove("PID");
return View("Index");
}
Now, when I logoff using the Logoff Action, I want the Authorize attribute of the Index to take effect but it doesn't when I return the Index View in the Logoff Action.
How would I handle this?
I'm using a custom MembershipProvider and not sure how to put Logoff functionality in it.
You should redirect, not return the view directly:
public ActionResult Logoff()
{
HttpContext.Session.Remove("LogonTicket");
HttpContext.Session.Remove("PID");
return RedirectToAction("Index");
}
Returning the view directly sends the view's markup to the client right away and the Authorize filter does not get invoked at all.
Done in the AccountController Logoff Action. They are public "globals" in a base controller now, too.
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
LogonTicket = null;
ParticipantID = null;
return RedirectToAction("Index", "Home");
}

ActionResult HttpPost need to access value

I am using MVC C#
Say I have the following ActionResult:
public ActionResult Create(string location)
{
...
View()
}
I need to use location primary in the [httppost]
[HttpPost]
public ActionResult Create(Employee employee)
{
...
// I need to access the value of location here but I dont' have access to the View
}
What is the best way of getting the value of location. I can create a viewmodel and pass that value to the View and then retreive it in the [HttpPost ]but I do not have access to the View as it is restricted.
There is a number of methods to pass data between controller methods in mvc. One of them is by using TempData.
You can save location in your GET-method
public ActionResult Create(string location)
{
TempData["location"] = location;
...
View()
}
and then retrieve it in your POST-method
[HttpPost]
public ActionResult Create(Employee employee)
{
var location = TempData["location"];
...
}
Although, using a viewmodel would be more preferrable.

Override controller Authorize attribute for viewresult

If the authorize attribute has been applied to the controller is it possible to allow unauthorized access to an action/viewresult inside that controller?
Say for example I didn't want authorization to occur on Test2 in the following:
[Authorize]
public class TestController : Controller
{
public ViewResult Test()
{
return View();
}
public ViewResult Test2()
{
return View();
}
}
Thanks in advance.
No, this is not possible. You will have to apply the Authorize attribute on the Test action and not on the controller. Another possibility is to put the Test2 action on another controller which is not decorated with this attribute.
Back in MVC 3 it appears it was indeed not possible to do (as mentioned Darin Dimitrov), but if anyone using MVC 4 (and up) comes across this question, he\she should be able to use AllowAnonymous filter to achieve the result. So the code would become:
[Authorize]
public class TestController : Controller
{
public ViewResult Test()
{
return View();
}
[AllowAnonymous]
public ViewResult Test2()
{
return View();
}
}

What name to use in Html.BeginForm for async controller

Let's say I have an async controller like that below ~
public class HomeController : AsyncController
{
public void LoadAync(DumpModel model) {
}
public ActionResult LoadCompleted(string a) {
return View("Index");
}
}
How can I invoke this method from View?
#using (Html.BeginForm("Load", "Home"))
I tried using this one but it doesn't work. I can invoke only when I change "Load" to "LoadAsync" in View. But "LoadCompleted" will never be called.
Am I missing something obvious?
Your spelling of Async in wrong and MVC is convention based.
public void LoadAsync(DumpModel model) {
}
You have a typo. It should be LoadAsync and not LoadAync.

Resources