Extract controller and action names from ActionResult - asp.net

In my view (or partial view), I want to use a helper that has this signature:
// using T4MVC to get the ActionResult from "MVC.FooController.BarAction()"
#Html.doStuff(MVC.FooController.BarAction(), someData)
instead of
#Html.doStuff(controllerName, actionName, someData)
I want to simplify my extension method, i.e. one argument instead of two.
In the extension method, how would I extract controller and action names from an instance of ActionResult?
Note that HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); doesn't help. I need to pass in an arbitrary ActionResult. So this is NOT a duplicate question

In View use below code -
#ViewContext.RouteData.Values["Controller"]
#ViewContext.RouteData.Values["Action"]
In Controller -
Request.RequestContext.RouteData.Values["controller"].ToString()
Request.RequestContext.RouteData.Values["action"].ToString()
In C# Helper Class -
HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString();
HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString();
If you want you retrieve Action and controller names from ActionResult, then one option would be -
// In Action
ViewBag.Controller = Request.RequestContext.RouteData.Values["controller"].ToString();
ViewBag.Action = Request.RequestContext.RouteData.Values["action"].ToString();
Then retrieve values from ActionResult in following way -
var controllerName = actionResultVariable.ViewBag.Controller;
var actionName = actionResultVariable.ViewBag.Action;
EDIT: Option 2 :
Create a Custom ActionResult -
public class MyActionResult : ViewResult
{
public MyActionResult(object model)
{
this.ViewData.Model = model;
}
public string ControllerName
{
get
{
return HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString();
}
}
public override void ExecuteResult(ControllerContext context)
{
base.ExecuteResult(context);
}
}
And then in your Controller Action -
return new MyActionResult(model);
Then as you are passing the ActionResult to your Extension method, you can use -
MyActionResultVariable.ControllerName

Related

Pass Multiple params in ASP.NET MVC

First of all, I'm still a beginner in MVC
Obviously anyone knows that we can pass by /CONTROLLER/METHOD/ID, but in some cases, like I need to pass 3-4 params into the controller, how should I do? Is there a good way of passing them all?
The code below is a controller dealing with post request, why I cannot use the "temp1"? it said that one should be declared before use, what that means?
[HttpPost]
public ActionResult Payment_Home(Wires_SWIFT temp1){
string str = temp1.BENEFICIARY_NAME;
DbQuery<Wires_SWIFT> dbq = db.Wires_SWIFT.Where(d => d.BENEFICIARY_NAME LIKE temp1.);
return View();
}
Use a ViewModel.
This is a class that contains all the values you need - you populate it in the controller action and pass it in to the view (helps if the view is strongly typed to the model.
public class MyModel
{
public string SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
// controller
var myModel = new MyModel...
return View(myModel);
// view
#model MyModel
#Model.SomeValue
You need to make sure you have a route set correctly.
so to match dothis/action/param1/param2 you'd want a route like:
routes.MapRoute(
"MyRoute", // Route name
"{controller}/{action}/{param1}/{param2}", // URL with parameters
new { controller = "Surveys", action = "Index", param1 = "param1", param2 = "param2" } // Parameter defaults
);
For the above route to actually work, you'll need a controller and action such as
public class DoThisController : Controller
....
public ActionResult Action(int param1, int param2)
....
The type of the action parameters doesn't matter, but the name should match the name defined in the rule to allow the action to be located by ASP.Net
In your case, the error is because your parameter is called temp1 & in the route data there is nothing called that.

How to retrieve values sent in post method in asp.net

net I am trying to take a value from a previous form typically in php it would be written like this
$name= $_POST ["Username"];
$pass= $_POST ["Password"];
how can I write this in asp.net
if you use GET
string usrnm = Request.QueryString["username"];
string pass = Request.QueryString["password"];
if you use POST
string usrnm = Request.Form["username"];
string pass = Request.Form["password"];
in web forms
if (Page.IsPostBack)
{
//access posted input as
Request.Form["input1"]
Request.Form["input2"]
Request.Form["input3"]
}
in mvc
[HttpPost]
public ActionResult myaction(strig input1,strig input1,strig input1)
{
//you can access your input here
return View();
}
or if you have view model for it, which is capable to take 3 inputs as
public class myViewmodleclass()
{
public string input1{get;set;}
public string input2{get;set;}
public string input3{get;set;}
}
controller action
[HttpPost]
public ActionResult myaction(myViewmodleclass mymodelobject)
{
//you can access your input here
return View();
}
so you using mvc, which has a nice model binding.
So you are using mvc which has nice model binding. You can simply have a proper model object. For your example
public class LoginInfo()
{
public string UserName{get;set;}
public string Password {get;set;}
}
in your controller
[HttpPost]
public ActionResult Logon(LoginInfo loginInfo )
{
// Do stuff with loginInfo
}

asp.net mvc custom attributes

I am trying to create a custom attribute in mvc to use it's parameters in a view as breadCrumb.
well, this is the code of the attribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BreadCrumbAttribute : Attribute {
public BreadCrumbAttribute(string title, string parent, string url) {
this._title = title;
this._parent = parent;
this._url = url;
}
#region named parameters properties
private string _title;
public string Title {
get { return _title; }
}
private string _url;
public string Url {
get { return _url; }
}
private string _parent;
public string Parent {
get { return _parent; }
}
#endregion
#region positional parameters properties
public string Comments { get; set; }
#endregion
}
this is the call of the attribute
[BreadCrumbAttribute("tile", "parent name", "url")]
public ActionResult Index() {
//code goes here
}
this is a way of how I'd like to get the values. (this is a partial view)
System.Reflection.MemberInfo inf = typeof(ProductsController);
object[] attributes;
attributes = inf.GetCustomAttributes(typeof(BreadCrumbAttribute), false);
foreach (Object attribute in attributes) {
var bca = (BreadCrumbAttribute)attribute;
Response.Write(string.Format("{0}><a href={1}>{2}</a>", bca.Parent, bca.Url, bca.Title));
}
Unfortunately, the attribute didn't get call with the way I implement it. Although, If I add the attribute in Class instead of an Action method it worked.
How could I make it work?
Thanks
The problem is that you are using reflection to get the attributes for the class, so naturally it does not include attributes defined on the action method.
To get those, you should define an ActionFilterAttribute, and in the OnActionExecuting or OnActionExecuted method, you can use filterContext.ActionDescriptor.GetCustomAttributes() method (MSDN description here).
Note that with this solution, you will likely have two different types of attributes: The first one is the one you wrote, to define the breadcrumbs. The second is the one that looks at the attributes on the executing action and builds up the breadcrumb (and presumably adds it to the ViewModel or sticks it in HttpContext.Items or something).

RedirectToAction alternative

I'm using ASP.NET MVC 3.
I've wriiten a helper class as follows:
public static string NewsList(this UrlHelper helper)
{
return helper.Action("List", "News");
}
And in my controller code I use it like this:
return RedirectToAction(Url.NewsList());
So after the redirect the link looks like this:
../News/News/List
Is there an alternative to RedirectToAction? Is there a better way that I need to implement my helper method NewsList?
Actually you don't really need a helper:
return RedirectToAction("List", "News");
or if you want to avoid hardcoding:
public static object NewsList(this UrlHelper helper)
{
return new { action = "List", controller = "News" };
}
and then:
return RedirectToRoute(Url.NewsList());
or another possibility is to use MVCContrib which allows you to write the following (personally that's what I like and use):
return this.RedirectToAction<NewsController>(x => x.List());
or yet another possibility is to use T4 templates.
So it's up to you to choose and play.
UPDATE:
public static class ControllerExtensions
{
public static RedirectToRouteResult RedirectToNewsList(this Controller controller)
{
return controller.RedirectToAction<NewsController>(x => x.List());
}
}
and then:
public ActionResult Foo()
{
return this.RedirectToNewsList();
}
UPDATE 2:
Example of unit test for the NewsList extension method:
[TestMethod]
public void NewsList_Should_Construct_Route_Values_For_The_List_Action_On_The_News_Controller()
{
// act
var actual = UrlExtensions.NewsList(null);
// assert
var routes = new RouteValueDictionary(actual);
Assert.AreEqual("List", routes["action"]);
Assert.AreEqual("News", routes["controller"]);
}

Modern way to handle and validate POST-data in MVC 2

There are a lot of articles devoted to working with data in MVC, and nothing about MVC 2.
So my question is: what is the proper way to handle POST-query and validate it.
Assume we have 2 actions. Both of them operates over the same entity, but each action has its own separated set of object properties that should be bound in automatic manner. For example:
Action "A" should bind only "Name" property of object, taken from POST-request
Action "B" should bind only "Date" property of object, taken from POST-request
As far as I understand - we cannot use Bind attribute in this case.
So - what are the best practices in MVC2 to handle POST-data and probably validate it?
UPD:
After Actions performed - additional logic will be applied to the objects so they become valid and ready to store in persistent layer. For action "A" - it will be setting up Date to current date.
I personally don't like using domain model classes as my view model. I find it causes problems with validation, formatting, and generally feels wrong. In fact, I'd not actually use a DateTime property on my view model at all (I'd format it as a string in my controller).
I would use two seperate view models, each with validation attributes, exposed as properties of your primary view model:
NOTE: I've left how to combining posted view-models with the main view model as an exercise for you, since there's several ways of approaching it
public class ActionAViewModel
{
[Required(ErrorMessage="Please enter your name")]
public string Name { get; set; }
}
public class ActionBViewModel
{
[Required(ErrorMessage="Please enter your date")]
// You could use a regex or custom attribute to do date validation,
// allowing you to have a custom error message for badly formatted
// dates
public string Date { get; set; }
}
public class PageViewModel
{
public ActionAViewModel ActionA { get; set; }
public ActionBViewModel ActionB { get; set; }
}
public class PageController
{
public ActionResult Index()
{
var viewModel = new PageViewModel
{
ActionA = new ActionAViewModel { Name = "Test" }
ActionB = new ActionBViewModel { Date = DateTime.Today.ToString(); }
};
return View(viewModel);
}
// The [Bind] prefix is there for when you use
// <%= Html.TextBoxFor(x => x.ActionA.Name) %>
public ActionResult ActionA(
[Bind(Prefix="ActionA")] ActionAViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Name, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionBViewModel
}
}
public ActionResult ActionB(
[Bind(Prefix="ActionB")] ActionBViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Date, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionAViewModel
}
}
}
One possible way to handle POST data and add validation, is with a custom model binder.
Here is a small sample of what i used recently to add custom validation to POST-form data :
public class Customer
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
public class PageController : Controller
{
[HttpPost]
public ActionResult ActionA(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
[HttpPost]
public ActionResult ActionB(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
}
A CustomerModelBinder will be something like that:
public class CustomerModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "Name") //or date or whatever else you want
{
//Access your Name property with valueprovider and do some magic before you bind it to the model.
//To add validation errors do (simple stuff)
if(string.IsNullOrEmpty(bindingContext.ValueProvider.GetValue("Name").AttemptedValue))
bindingContext.ModelState.AddModelError("Name", "Please enter a valid name");
//Any complex validation
}
else
{
//call the usual binder otherwise. I noticed that in this way you can use DataAnnotations as well.
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
and in the global.asax put
ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());
If you want not to bind Name property (just Date) when you call ActionB, then just make one more custom Model Binder and in the "if" statement, put to return the null, or the already existing value, or whatever you want. Then in the controller put:
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerAModelBinder))] Customer customer)
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerBModelBinder))] Customer customer)
Where customerAmodelbinder will bind only name and customerBmodelbinder will bind only date.
This is the easiest way i have found, to validate model binding, and i have achieved some very cool results with complex view models. I bet there is something out there that i have missed, and maybe a more expert can answer.
Hope i got your question right...:)

Resources