ASP.NET MVC 3 - Passing variable model to controller - asp.net-mvc-3

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.

Related

Create ViewData outside Controler in MVC

I have a method inside a controller that creates ViewData. For example this one
private void CreateFKViewData(OSQDCOL osqdcol, OSADCOL osadcol, IList<OSADCOL> targetTBLcols)
{
...
ViewData[osadcol.ColName] = new SelectList(SelectListItems, "key", "value", SelectListItems.First().Key);
}
If that method is placed inside the controller everything works as expected. I want to move that method outside my controller and place it into a different class in my BLL layer. The problem is that ViewData is not accessible outside the scope of the controller.
Any ideas?
I'm assuming you're using ASP.NET MVC and C#. Not sure it's a great idea to spread around concerns that a controller would normally do into classes outside of the controller, but suffice to say that the reason for your issue is that ViewData is made available by the fact that your controller class inherits from Controller which in turn inherits from ControllerBase (which is where ViewData is provided). So let's say you wanted to call a method in another class from your controller, and you wanted that method to be able to manipulate ViewData. Consider the following:
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
var externalclass = new SomeRandomClass(this);
externalclass.DoStuff();
return View();
}
}
public class SomeRandomClass
{
ControllerBase _callingController = null;
public SomeRandomClass(ControllerBase callingController)
{
this._callingController = callingController;
}
public void DoStuff()
{
this._callingController.ViewData["hello"] = "world";
}
}

ASP.Net Web API - How can I make it so that prefixes are not required when model binding from the query string?

In ASP.Net Web API (RC) I have a test model class like so:
[ModelBinder]
public class TestRequest
{
public string Foo { get; set; }
public string Bar { get; set; }
}
My controller looks like this:
public class TestController : ApiController
{
public TestRequest Get(TestRequest model)
{
return model;
}
}
Now if I invoke the action via:
http://.../test?foo=abc&bar=xyz
neither values bind, because the model binder is expecting model prefixes, such that I actually need to call:
http://.../test?model.foo=abc&model.bar=xyz
I can understand that this is so that other action parameters can bind correctly, but in my case the model is a clean way of encapsulating all the possible action parameters so that I don't need to have a nasty action method signature with a whole lot of optional parameters. It also allows for easy model validation.
Is there any easy way to cause model binding to behave the same way as it would in MVC, or in a POST request?
Removing the ModelBinder attribute from your model class should work in the example you've posted. You'll run into issues for more complex method signatures, see Rick Strahl's comment: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx#10302750

MVC 3 split parameters in HttpPost action

I have an MVC 3 app and I have created a generic wrapper object, which has some navigation properties and the wrapped object of T, whose values I'm editing/displaying.
public class NavigationViewModel<T>
{
public T Model { get; set; }
public NavigationHelper NavigationHelper { get; set; }
public NavigationViewModel() { }
public NavigationViewModel(T model, NavigationHelper helper)
{
this.Model = model;
this.NavigationHelper = helper;
}
}
My controller resolves this object nicely in an action like this:
public ActionResult Foo(NavigationViewModel<Bar> viewModel)
Code in my view looks like this:
#Html.EditorFor(model => model.Model.SomeProperty)
My colleague said that that code is not nice to read. I already have a strongly typed view, the Model and this Model has another property called Model. He suggested to rename the Model property to ViewModel and I agreed with his reasoning.
Now, the code with the renamed properties does not work anymore: NavigationViewModel viewModel is null. So I changed the signature of the HttpPost method to the following and it works again:
[HttpPost]
public ActionResult Foo(NavigationHelper helper, Bar viewModel)
I like this very much! I can directly access my viewModel in code, the code in the view makes sense and the helper object does not get in the way. I haven't seen this convention before and I guess it worked before because of the naming convention. Using a property called Model hinted at how to resolve the object. Without that property, it couldn't resolve it anymore.
I would like to adopt this for other kinds of helpers that contain view-specific properties, like select-lists or other properties that I otherwise might have put in my ViewBag. Would you guys recommend this approach or will I run into trouble later on using this?
I think I have a really simple answer for you, just don't name your action parameter viewModel, so change:
public ActionResult Foo(NavigationViewModel viewModel)
public ActionResult Foo(NavigationViewModel model)
Or any other parameter name that does not collide with your ViewModel property on your NavigationViewModel class.

Is there a way to pass back the Request.Files in the view model on post (ASP.NET MVC 3)?

I've successfully removed references to Request.Form in my code, but I haven't seen any ASP.NET MVC 3 support for binding Request.Files to my view model. Would best practice be to just pass the Request.Files object (HttpFileCollectionBase) to whatever method processes my attachments? E.g.,
[HttpPost]
public ActionResult UpdateStatus(StatusViewModel vm)
{
bool updated = HandleUpdate(Request.Files, vm);
...
return View("Updated");
}
You can simply declare Action parameters of type HttpPostedFileBase, like so:
public ActionResult Method(HttpPostedFileBase fileUploaded)
You can bind to arrays of like-named instances, as well.
public ActionResult Method(HttpPostedFileBase[] filesUploaded)
and, HttpPostedFileBase can be a member of your view model.
public class MyModel{
public string Name{get; set;}
public HttpPostedFileBase FileUploaded{get; set;}
}

Catching and resolving AmbiguousMatchException

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)
{
...
}

Resources