0 Comments

Today, one of the colleague asked me a question about how to post a form with a complex object.  It is actually a bit tricky. This is because of the nature of HTML form [1].  When a form is submitted for processing, some controls have their name paired with their current value and these pairs are submitted with the form.  Those controls for which name/value pairs are submitted are called “successful controls”

 

HTML defines the following control types:

checkbox– When a form is submitted, only “on” checkbox controls can become “successful control”

Radio button – Radio button are like checkbox.

Input– the input text becomes the control’s current value.

hidden controls– Authors may create controls that are not rendered but whose values are submitted with a form. it generally sued for storing information between client/server exchanges. The Input element is used to create a hidden control.

 

The problem my colleague had is that  he wants to pass the view model to his controller by submitting the form.   However, the model is a complex model, after submitting the form. Only those simple type property has values, but value of the property like ICollection is null.  As explained above,  This is because that it is not a name/value pair value.  For solving this issue, we think  what if we convert the complex object into a serialized object and set it as a hidden control value.  So, after submitting the form, the value will be passed to the server.

Thus, we added the code as below :

@using(Html.BeginForm(…))

{

     @Html.Hidden("reportViewModel", JsonConvert.SerializeObject(Model))

}

Then, the action parameter is expecting a string. However, we still want to use a strong type object.  In order to do that, we end up with creating a custom model binder. Thanks to Mike Stall.

 

Follow the steps below to create custom model binder:

Step 1 – Create Model Binder

public class ReportViewModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

{
     string key = bindingContext.ModelName;
     ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);

if (val != null)
{
        string s = val.AttemptedValue as string;

        if (s != null){

               return JsonConvert.DeserializeObject<ReportViewModel>(s);
      }
             }
             return null;
         }
}

Step 2 – Add [ModelBinder] attribute on the parameter’s signature

public FileContentResult ReportSpreadshee([ModelBinder(typeof(ReportViewModelBinder))]ReportViewModel reportViewModel){

}

After all the efforts, we finally can pass the complex object with post method, when submitting a form.  Please feel free to make a comment, if you have an optimised solution.

References

[1]https://www.w3.org/TR/REC-html40/interact/forms.html#successful-controls

[2]https://blogs.msdn.microsoft.com/jmstall/2012/04/20/how-to-bind-to-custom-objects-in-action-signatures-in-mvcwebapi/

0 Comments
Nowadays, WebAPI is widely used for building a RESTful service. Given the fact that Web API can automatically serialize model to JSON format, so Json becomes a very common/popular argument for WebAPI.

The question is how to effectively validate the Json object before processing your business logic. Normally, you might do something like this

if (string.isNullEmpty(object.a)){
//your logical here
}

Thinking if you have 10 properties, you will have to check 10 times and the code will be pretty nasty. So the solution below is attempted to provide an elegant way for validating the JSON object.

Solution
Implement custom ValidationAttributeto the property. Look at the example below, NotNullableAttribute is a customValidationAttribute which will evaluate the value. If it's null, it will throw a NoNullAllowedException.

For example:

[NotNullable(ErrorMessage = "product field can not be null")]
public string Product { get; set; }

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NotNullableAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value ==null){
//place your logical here
throw new NoNullAllowedException(ErrorMessage);
}

return true;
}
}

If you don't want to write your own code, here is theNuGet package, or you can run the following command in the Package Manager Console.

PM> Install-Package ObjectPropertyValidator


Usage of the NuGet Package

1. Step 1
Create your own class
public class Customer
{
[NotNullable]
public string Name { get; set; }

[NotEmptyWithValidData( Pattern= @"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$")]
public string Email { get; set; }

[NotEmptyWithValidData(MaxLength =10, MinLength = 10)]
public string Phone { get; set; }
}

2.Step 2
Create your WebAPI.

[HttpPost]
public HttpResponseMessage Post(Customer customer)
{
// the argument customerwill be validated before reach your code below.
}
Conclusion
As you can see above, with the solution less code is required for data validation, and it's completely reusable.