Can I reuse a remote validation action in MVC3 - asp.net-mvc-3

I am using a Remote validation attribute on my view model to validate a Bank Account that is specified for my Company:
ViewModel:
[Remote("CheckDefaultBank", "Company")]
public string DefaultBank
{
This in the controller I have:
[HttpGet]
public JsonResult CheckDefaultBank(string defaultBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
That all works well. But, I have two other banks related to my company as well. However, when the remote validation js calls the action it uses a parameter mactching the field name of "DefaultBank"... so I use that as a parameter in my action.
Is there some attribute I can add in the view so that it will use a parameter of say "bankId" on the ajax get so I don't need an action for each field which are basically exactly the same?
The goal here is to eliminate now having to have this in my controller:
[HttpGet]
public JsonResult CheckRefundBank(string refundBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public JsonResult CheckPayrollBank(string payrollBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
I was hoping I could do something like this in the view:
#Html.EditorFor(model => model.DefaultBank, new { data-validate-parameter: bankId })
This way I could just use the same action for all of the Bank entries like:
[HttpGet]
public JsonResult CheckValidBank(string bankId)
{
bool result = BankExists(bankId);
return Json(result, JsonRequestBehavior.AllowGet);
}
Possible?

For just such a situation, I wrote a RemoteReusableAttribute, which may be helpful to you. Here is a link to it: Custom remote Validation in MVC 3

Since MVC uses the default model binder for this, just like a normal action method. You could take a FormsCollection as your parameter and lookup the value. However, I personally would find it much easier to just use several parameters to the function, unless you start having dozens of different parameters.
You could also write a custom model binder, that would translate the passed parameter to a generic one.

Consider encapsulating the logic, "BankExists" in this case into a ValidationAttribute (Data Annotations Validator). This allows other scenarios as well.
Then use a wrapper ActionResult like the one below, which lets you pass in any validator.
[HttpGet]
public ActionResult CheckRefundBank(string refundBank)
{
var validation = BankExistsAttribute();
return new RemoteValidationResult(validation, defaultBank);
}
Here is the code for the ActionResult that works generically with Validators.
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class RemoteValidationResult : ActionResult
{
public RemoteValidationResult(ValidationAttribute validation, object value)
{
this.Validation = validation;
this.Value = value;
}
public ValidationAttribute Validation { get; set; }
public object Value { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var json = new JsonResult();
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
if (Validation.IsValid(Value))
{
json.Data = true;
}
else
{
json.Data = Validation.FormatErrorMessage(Value.ToString());
}
json.ExecuteResult(context);
}
}
As an extra enhancement consider creating a Controller Extension method to dry up your return call even more.

Related

DataSourceRequest is not deserializing for a WebAPI Get method

I am trying to call a WebAPI method from Angular 5 like this:
selectClaims(state: DataSourceRequestState):Observable<DataResult>
{
return this.http.get<GridDataResult>(`${this.apiUrl}/SelectClaims?${toDataSourceRequestString(state)}`);
}
Which calls the API method as expected. The API method is:
[Route("SelectClaims")]
[HttpGet]
public IHttpActionResult SelectClaims([FromUri][DataSourceRequest]DataSourceRequest ClaimsRequest)
{
if(ClaimsRequest == null)
ClaimsRequest=new DataSourceRequest { Page=1, PageSize=20 };
var result = _db.Claims.ToDataSourceResult(ClaimsRequest, c => { c.SortHistory(); return c; });
return Ok(result);
}
The trouble is that ClaimsRequest only de-serializes Page and PageSize correctly. Filters and Sorts don't come through:
Fiddler tells me that the URL from Angular is:
GET /api/v1/Claims/SelectClaims?filter=id~eq~2&page=1&sort=firstName-asc&pageSize=20 HTTP/1.1, but in the controller both filter and sort are null.
If I create a URL through Swagger like: 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.sorts=firstName-desc' I do see a sort array in the API method, but the "Member" field is null.
Any attempt to add a filter through Swagger like 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.filters=id~eq~2' results in a "Cannot create an instance of an interface." error.
The state is a DataSourceRequestState in the angular component from a Kendo Grid for Angular.
I have simulated this in a simple test program and everything works fine there. The only difference in my test program is that the API controller targets .Net Core and the real system targets .Net 4.6.1.
Do I have to de-serialize manually in .Net 4.6.1 for some reason, or is something else going on here?
It should be a POST not a GET. Something like this:
return this.http.post<GridDataResult>(`${this.apiUrl}/SelectClaims`, toDataSourceRequestString(state)});
I needed it to be a GET (URL) so i created a new object
public class GridParamaterBinder
{
public int Page { get; set; }
public int PageSize { get; set; }
public string Filter { get; set; }
public string Sort { get; set; }
public DataSourceRequest ToDataSourceRequest(IConfigurationProvider mapper, Func<string, string> OverDefaultParamaterMapping)
{
DataSourceRequest result = new DataSourceRequest();
result.Page = Page;
result.PageSize = PageSize;
result.Sorts = GridDescriptorSerializer.Deserialize<SortDescriptor>(Sort);
result.Filters = FilterDescriptorFactory.Create(Filter);
return result;
}
}
and used it instead of the Telerik effort.
in API I Bind it like so
public virtual DataSourceResult Get([FromUri]GridParamaterBinder request)
And then used it like
DataSourceResult results = query.ToDataSourceResult(request.ToDataSourceRequest(), r => (r)));
Thanks #KevDevMan for your solution. I found this example,
then I changed my API controller like this and it worked like a charm :
[HttpGet, Route("for-kendo-grid")]
public DataSourceResult GetProducts([System.Web.Http.ModelBinding.ModelBinder(typeof(WebApiDataSourceRequestModelBinder))] DataSourceRequest request)
explanation here

Return raw objects from Action methods and convert them to JsonResult before rendering

The website that I'm working on is heavily depending on ajax/json and knockout.js.
I would like to have a lot of my Controllers return view-tailored 'json objects', without wrapping them in a JsonResult when returning the method.
This would mean I could easily composite multiple calls into one parent object, but still be able to call the Actions separately too.
Simplified example:
public object Main(int groupId)
{
var viewModel = new
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public object Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public object AllMessages()
{
return DatabaseContext.Messages.ToList();
}
I was hoping I could capture the returned object in OnActionExecuted and at that point wrap the whole result up in a final JsonResult.
The result is already converted to a string and captured in a ContentResult though.
Any ideas? :) Thanks,
A good approach on this is to create helper methods for your entity calls. Or if you have those methods already somewhere, they can actually serve as the helper methods. In that manner you can return a list of strongly-typed Messages and Employees as well as returning your desired parent object. You can then have individual controller methods that returns json objects. In addition, you can extend the parent viewmodel to return additional fields.
The Parent ViewModel
public class ParentModel {
public Employee Persons {get;set;}
public Message Messages {get;set;}
}
The Helper Methods
The beauty of using helper methods similar to what is defined here is that you can apply a few more logic to your query, and more, and you don't have to change anything in your controller methods.
public ParentModel GetMain(int groupId)
{
var viewModel = new ParentModel
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public IEnumerable<Employee> Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public IEnumerable<Message> AllMessages()
{
return DatabaseContext.Messages.ToList();
}
The Controller Methods
public ActionResult GetParent(int groupId){
return Json(helperinstance.GetMain());
}
public ActionResult GetEmployees(int groupId){
return Json(helperinstance.Employees());
}
public ActionResult GetMessages(int groupId){
return Json(helperinstance.AllMessages());
}
Thanks for the answer. I'm not going for the solution of von v. because I like to keep the boilerplate as small as possible.
In the end I am trying out the following approach. It seems to work pretty well for now, but I still have to test it in real production.
If anyone has some (security) concerns with this, I'm happy to hear them in the comments.
// BaseController
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var returnType = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType;
// is the returnType not deriving from ActionResult? Automatically wrap it in a JsonResult
if ( !typeof(ActionResult).IsAssignableFrom(returnType) )
{
var result = filterContext.ActionDescriptor.Execute(filterContext, filterContext.ActionParameters);
filterContext.Result = Json( result );
}
}

ASP.NET MVC 3 Viewmodel Pattern

I am trying to work out the best way of using a viewmodel in the case of creating a new object.
I have a very simple view model that contains a contact object and a select list of companies.
private ICompanyService _Service;
public SelectList ContactCompanyList { get; private set; }
public Contact contact { get; private set; }
public ContactCompanyViewModel(Contact _Contact)
{
_Service = new CompanyService();
contact = _Contact;
ContactCompanyList = GetCompanyList();
}
private SelectList GetCompanyList()
{
IEnumerable<Company> _CompanyList = _Service.GetAll();
return new SelectList(_CompanyList, "id", "name");
}
I then have contact controller that uses this viewmodel and enable me to select a related company for my contact.
[Authorize]
public ActionResult Create()
{
return View(new ContactCompanyViewModel(new Contact()));
}
My issue is with the create method on the controller.
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Contact _Contact)
{
try
{
_Service.Save(_Contact);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The problem is that the view returns an empty contact object, but! the company id is populated, this is because the dropdown list explicitly declares its field name.
#Html.DropDownList("parent_company_id",Model.ContactCompanyList)
The standard html form fields pass the objects values back in the format of contact.forename when using the HTML.EditorFor helper...
#Html.EditorFor(model => model.contact.forename)
I can access them if I use a FormCollection as my create action method paremeter and then explicitly search for contact.value but I cannot use a Contact object as a parameter to keep my code nice and clean and not have to build a new contact object each time.
I tried passing the actual view model object back as a parameter but that simply blows up with a constructor error (Which is confusing seeing as the view is bound to the view model not the contact object).
Is there a way that I can define the name of the Html.EditFor field so that the value maps correctly back to the contact object when passed back to the create action method on my controller? Or Have I made some FUBAR mistake somewhere (that is the most likely explanation seeing as this is a learning exercise!).
Your view model seems wrong. View models should not reference any services. View models should not reference any domain models. View models should have parameterless constructors so that they could be used as POST action parameters.
So here's a more realistic view model for your scenario:
public class ContactCompanyViewModel
{
public string SelectedCompanyId { get; set; }
public IEnumerable<SelectListItem> CompanyList { get; set; }
... other properties that the view requires
}
and then you could have a GET action that will prepare and populate this view model:
public ActionResult Create()
{
var model = new ContactCompanyViewModel();
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
and a POST action:
[HttpPost]
public ActionResult Create(ContactCompanyViewModel model)
{
try
{
// TODO: to avoid this manual mapping you could use a mapper tool
// such as AutoMapper
var contact = new Contact
{
... map the contact domain model properties from the view model
};
_Service.Save(contact);
return RedirectToAction("Index");
}
catch
{
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
}
and now in your view you work with your view model:
#model ContactCompanyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedCompanyId, Model.CompanyList)
... other input fields for other properties
<button type="submit">Create</button>
}

How to add a bind list to the TryUpdateModel in asp.net mvc3

I have the following action method:-
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Assessment a = elearningrepository.GetAssessment(id);
try
{
if (TryUpdateModel(a))
{
elearningrepository.Save();
return RedirectToAction("Details", new { id = a.AssessmentID });
}
}
//code does here
but I can not write something like if (TryUpdateModel(a, "Assessment", new string { "Date"})) to specify that I only allow the Date property to be updated.
So how I can add a bind list to the above if (TryUpdateModel(a))?
BR
but I can not write something like
if (TryUpdateModel(a, "Assessment", new string { "Date"}))
That's because you should write it like this, since the allowed properties argument represents a string array:
if (TryUpdateModel(a, "Assessment", new[] { "Date" }))
{
}
I would suggest that you stay away from using TryUpdateModel in general.
The repository usually has an update method that sets the entityState to modified before Save() is called, i cannot see that in the code above.
If your goal is to display a record and only allow date to be saved, then create a view for that model, and render fields with:
This sets the model for the view:
#model YourNamespace.Models.Assessment
#Html.DisplayFor(model=>model.propertyToDisplay)
on the items you only want to display, and a
#Html.EditorFor(model=>model.Date)
In your action controller you take the properties you want to bind to as input parameters:
Edited
class Assessment
{
public int Id { get; set; }
public DateTime Date { get; set; }
//Other properties
}
public ActionResult Edit(int Id, DateTime Date)
{
var assessment = elearningrepository.GetAssessment(id);
assessment.Date = Date;
elearningrepository.UpdateAssessment(assessment);
elearningrepository.Save();
//Redirect to action Detail
}
In this case the model binder should just bind to Id, and Date, so even if someone tries to post other values (editing the html form is easy), parameters in ActionResult should be named exactly as in the Model and use that to fetch and update the entity.
You should validate that the user actually can access and edit that id, or as an alternative use MVC Security Codeplex to check that the Id parameter has not been tampered with. it is really easy and convenient to use, but that is another discussion.
As an alternative you can use an attribute like this, described in this blog, but I don't use that myself:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create( [Bind(Include="Id,Date")] Assessment assessment)
i tried this an it works fine
string[] allowedProperties = new[] { "Date" };
try
{
if (TryUpdateModel(a, allowedProperties))
{

ASP MVC 3 testing controller calling ModelState.IsValid always returns true

I have an ASP MVC 3 application and in my Model I have implemented IValidatableObject.
When my controller posts for a create or edit, I obviously only want to save the model if it is valid.
I see many blogs and posts and answers that say something like
if(!ModelState.IsValid)
{
return View();
}
My question. Why is it that ModelState.IsValid is always true in a unit test on the Controller?
Example:
[Test]
public void InValidModelsAreNotAdded()
{
var invalidModel = new MyModel() { SomeField = "some data", SomeOtherField = "" };
var result = _controller.Submit(invalidModel);
_repository.AssertWasNotCalled(r => r.Add(Arg.Is.Anything));
}
Model code:
public class MyModel : IValidatableObject
{
public string SomeField { get; set; }
public string SomeOtherField { get; set; }
public IEnumerable Validate(ValidationContext validationContext)
{
if(string.IsNullOrWhiteSpace(SomeOtherField))
{
yield return
new ValidationResult("Oops invalid.", new[] {"SomeOtherField"});
}
}
}
The AssertWasNotCalled always fails this test.
I stepped through the test and noticed that the ModelState.IsValid is true for this test. It is as if the IValidatableObject.Validate is not being invoked. It seems to work when I run the project, but thats not much of a way to test drive an application.
Also, I realize I could use the [Required] attribute for my example, but my real code has much more complex validation to it.
Thoughts?
It's true because you haven't called anything which sets it false.
This normally happens during binding, but since you just pass the model directly in the test you skip that altogether.
If you're trying to test validation, do that directly. If you're trying to test the error path in your controller, your test's arrange can call _controller.ModelState.AddModelError( //...
Well, insted of simulate the model binding behavior you can do that:
public class YourController : Controller
{
//some code
public ViewResult someAction(Model model)
{
try
{
ValidateModel(model);
}
catch
{
// deal with errors
}
}
//some code
}
ValidateModel with "try catch" blocks are much more readable for me. But you can still use "if" blocks with the method TryValidateModel
Hope that helps!!

Resources