Map post parameters to a model - c#

I have a model I want to use for communication with an external web service. It's supposed to call a specific post action on my website.
public class ConfirmationModel{
...
public string TransactionNumber {get; set;}
}
public ActionResult Confirmation(ConfirmationModel){
...
}
The problem is the parameters names they pass are not very human-readable. And I want to map them to my more readable model.
't_numb' ====> 'TransactionNumber'
Can this be done automatically? With an attribute maybe? What's the best approach here?

Create a model binder:
using System.Web.Mvc;
using ModelBinder.Controllers;
public class ConfirmationModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = new ConfirmationModel();
var transactionNumberParam = bindingContext.ValueProvider.GetValue("t_numb");
if (transactionNumberParam != null)
model.TransactionNumber = transactionNumberParam.AttemptedValue;
return model;
}
}
Initialise it in Global.asax.cs:
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(ConfirmationModel), new ConfirmationModelBinder());
}
Then in your action method
[HttpPost]
public ActionResult Confirmation(ConfirmationModel viewModel)
You should see the value of t_numb appear in TransactionNumber property of the viewmodel.

Agree that model binder is better: here's an alternate idea though
public ActionResult Create(FormCollection values)
{
Recipe recipe = new Recipe();
recipe.Name = values["Name"];
// ...
return View();
}
and a good read about both: http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx

Related

How to access Model object in ActionFilterAttribute.OnResultExecuted?

I am working with a WEB API application in ASP .NET Core 2.0 where I have a custom filter attribute that inherits from ActionFilterAttribute.
How can I access the Model object passed to a controller action in POST call, in ActionFilterAttribute.OnResultExecuted()
The mentioned method is passed a ResultExecutedContext object but I could not find an easy and reliable way to get Model object from it.
The filter I have, looks like the following:
public sealed class MyCustomFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnResultExecuted(ResultExecutedContext context)
{
var model = ?????????????
}
public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
}
}
and the controller looks like the following:
[Route("api/[controller]")]
public class MyController : Controller
{
[ServiceFilter(typeof(MyCustomFilter))]
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<MyModel> data)
{
// my logic to process model here. In my filter, I want to access data, which is passed into this Post method
return Ok();
}
}
I set a private var in the controller and assign the posted model to it on the controllers action
private YourModel _yourModel;
public ActionResult MyAction(YourModel model)
{
_yourModel = model;
return View();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
//Access _yourModel here
}
To access the model from the filterContext parameter passed in, you can use the below
var model = ((Controller)filterContext.Controller).ViewData.Model;
Try using TempData. Set tempdata to the model in your action.
TempData["Model"] = myModel;
Then access using the TempData on the context.
var x = ((Controller)filterContext.Controller).TempData["Model"];
Hope that's what you meant.
thanks

asp.net mvc web api change parameter model binding

public class UsernameBinder: System.Web.Http.ModelBinding.IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var key = bindingContext.ModelName;
var val = bindingContext.ValueProvider.GetValue(key);
if (val != null)
{
//?????????????????????????????????????????
//Get username property of user
//Set username property = User.Identity.Name
}
return false;
}
}
I want to create a model binder that binds username of user automatically. But I cauld not get property.
And I will use it like this:
public SendMessage CreateMessage([ModelBinder(typeof(UsernameBinder))]Message message)
{
}
How can I get property of a model from Web API?
I would suggest reading http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api this should help you to bind the model of your action method to your User. Now to get the user in WebApi uses a slighty different convetion (for WebApi NOT WebApi 2)
Such as
var username = ((ApiController)context.ControllerContext.Controller).User.Identity.Name;
I do not know if I really understood your question, but I solved a problem like this a shot time ago.
Problem: how to get the currentUserId in WebAPI controller actions?
// ex: update method
public IHttpActionResult Put(int id, [FromBody] MyModel model, Guid userId)
Solution:
Declare a binding and attribute:
public class CurrentUserIDParameterBinding : HttpParameterBinding
{
public CurrentUserIDParameterBinding(HttpParameterDescriptor parameter)
: base(parameter)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, CancellationToken cancellationToken)
{
Guid userId = /* my userid based on provider */;
actionContext.ActionArguments[Descriptor.ParameterName] = userId;
return Task.FromResult<object>(null);
}
}
public class CurrentUserIDAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return new CurrentUserIDParameterBinding(parameter);
}
}
and use it like this:
public IHttpActionResult Put(int id, [FromBody] MyModel model, [CurrentUserID] Guid userId)
may be you want to add custom parameter binding rule?
like this:
config.ParameterBindingRules.Insert(0, GetCustomParameterBinding);
see example How to pass ObjectId from MongoDB in MVC.net

Alternate names for querystring parameters

Is it somehow possible to set alternate names for querystring parameters in ASP.NET MVC?
I have this simple controller Index action:
public ActionResult Index(color = "")
{
...
}
Calling http://mysite.com/mypage/?color=yellow works quite nicely, the color parameter automatically picks up its value "yellow" from the querystring.
But now I would like to have a localized variant of the same page, with “pretty” localized parameters, but still working with the same controller method. Example: http://mysite.com/mypage/?farve=gul. Here I would like “gul” to be passed in as the color parameter to the default Index() ation method.
How do I set mappings for alternate names for querystring parameters?
How do I set mappings for alternate names for querystring parameters?
You could write a custom model binder.
So as in every ASP.NET MVC application you start by writing a view model:
public class MyViewModel
{
public string Color { get; set; }
}
and then a model binder for this model:
public class MyViewModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var query = controllerContext.HttpContext.Request.QueryString;
var value = query["color"] ?? query["gul"] ?? query["couleur"];
return new MyViewModel
{
Color = value,
};
}
}
which will be registered at your Application_Start:
ModelBinders.Binders.Add(typeof(MyViewModel), new MyViewModelBinder());
and now your controller action may take the view model as parameter:
public ActionResult Index(MyViewModel model)
{
...
}
Of course you could make the model binder more flexible by using some custom attribute on the property:
public class MyViewModel
{
[PossibleQueries("color", "gul", "couleur")]
public string Color { get; set; }
}
and in the model binder read those values and try reading them from the query string until you find one that is not null.
How about a second controller with a localized/pretty name where the actions and parameters have localized names and call the actions from the default/english controller? With this method you have all parts of the url localized.
Controller mypage
{
ActionResult Index(string color)
{
// normal code
}
}
Controller meineseite
{
ActionResult Index(string farbe)
{
return mypage.Index(farbe);
}
}

ASP.NET MVC Model Binding

If i have a Controller Action that may recieve both HTTP GET and HTTP POST from a number of different sources with each source sending different data e.g.
Source1 performs a form POST with two form items Item1 and Item2
Source2 performs a GET where the data is contained in the query string (?ItemX=2&ItemY=3)
Is it possible to have a controller action that will cater for all these cases and perform binding automatically e.g.
public ActionResult Test(Dictionary data)
{
// Do work ...
return View();
}
Is this possible with a custom binder or some other way?
Dont want to work directly with HttpContext.Request if possible
The usual pattern is to have two controller methods One controller method handles the GET, the other controller method handles the POST:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MyControllerMethod(string itemX, string itemY)
{
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyControllerMethod(MyViewDataObject data)
{
}
If you need help binding lists, collections or dictionaries you can find it here.
This solution works, not best for unit testing
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
TestIBE.Models.IBERequest _IBERequest;
HttpContextBase _httpContext;
Dictionary<string, string> _requestData;
_httpContext = controllerContext.HttpContext;
_requestData = this.CreateRequestData(_httpContext.Request);
_IBERequest = new TestIBE.Models.IBERequest(
_httpContext.Session.SessionID,
_httpContext.Request.UserHostAddress,
_httpContext.Request.UserAgent,
_httpContext.Request.Url,
_requestData);
return _IBERequest;
}
private Dictionary<string, string> CreateRequestData(
HttpRequestBase subject)
{
Dictionary<string, string> _result;
_result = new Dictionary<string, string>();
subject.Form.AllKeys.ForEach(key => _result.Add(key, subject.Form[key]));
subject.QueryString.AllKeys.ForEach(key => { if (!_result.ContainsKey(key)) { _result.Add(key, subject.QueryString[key]); } });
return _result;
}
public class IBEController : Controller
{
public ActionResult Landing(
[ModelBinder(typeof(TestIBE.Helpers.Binders.IBEModelBinder))] TestIBE.Models.IBERequest IBERequest)
{
// TODO
return View();
}
}

MVC custom viewmodel problems

I'm a bit of an MVC newbie, so you'll have to forgive what I imagine is an elementary question.
I created a custom viewmodel in order to have a multiselect list in my form:
public class CustomerFormViewModel
{
public Customer Customer { get; private set; }
public MultiSelectList CustomerType { get; private set; }
public CustomerFormViewModel(Customer customer)
{
Customer = customer
// this returns a MultiSelectList:
CustomerType = CustomerOptions.Get_CustomerTypes(null);
}
}
I found that my first attempt only captured the first value of the multiselect, and I guessed that this is because my create actions looked like this:
// GET: /Buyer/Create
public ActionResult Create() { ... }
// POST: /Buyer/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Customer customer) { ... }
So, I decided to change it to this:
// GET: /Buyer/Create
public ActionResult Create() { ... }
// POST: /Buyer/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(CustomerFormViewModel model) { ... }
So that I can get the full output from the MultiSelectList and parse it accordingly. Trouble is, this complains that there's no parameterless constructor for the viewmodel (and there isn't) - and I'm not sure the right way to go about fixing this. Nothing I've tried has worked and I really need some help!
In case it helps, my view looks like this:
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MySite.Controllers.CustomerFormViewModel>" %>
...
<% using (Html.BeginForm())
<%= Html.ListBox("CustomerType", Model.CustomerType)%>
...
Have you tried a custom ModelBinder. Not sure I'm understanding your code clearly but this could be your starting point:
public class CustomerFormViewModelBinder : DefaultModelBinder
{
protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var model = new CustomerFormViewModel(customer)
}
}
I believe I got it:
public class CustomerModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var form = controllerContext.HttpContext.Request.Form;
Customer customer = base.BindModel(controllerContext, bindingContext) as Customer;
if (customer!= null)
{
customer.CustomerType= form["CustomerType"];
}
return customer;
}
}
Along with an entry in the global.asax file's Application_Start():
ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());
which puts the comma separated list of listbox selections in the field. e.g. "1,3,4".

Categories