ASP. NET Web API Model-ModelBinder,
ASP. NET Web API Model-ModelBinder Preface
This article will introduce you to ASP. NET Web API ModelBinder binding principles and some object models involved, as well as simple Model binding examples, explained the Model metadata and ValueProvider modules in the previous section, then, the Model-bound modules in this article will be used in combination with the ParameterBinder module in the subsequent sections, that is, in ASP. NET Web API framework is bound to parameters through ParameterBinder. However, there are two ways to bind parameters in ParameterBinder, today, we will explain how to bind a Model to a single functional module.
Model-ModelBinder
Go directly to the topic. First, let's take a look at the definition of the IModelBinder interface type. All the ModelBinder functional modules have implemented the IModelBinder interface, as shown in the sample code 1-1.
Sample Code 1-1
IModelBinder
namespace System.Web.Http.ModelBinding{ public interface IModelBinder { bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext); }}
In code 1-1, we can see that the BindModel () method defines two parameters, both of which are context parameters. The first context parameter indicates the operation context, it is created before the Controller method is executed and encapsulates the necessary information for subsequent operations and the result values for storing request, response, and parameter binding. This will be explained later, it plays a very important role.
The second context parameter is to bind the context parameter. This is easy to understand. It means that the object encapsulates the information of the object to be bound, that is, information such as Model metadata and ValueProvider, it doesn't matter if you don't understand it now. You will understand it later.
HttpActionContext
Code 1-2
namespace System.Web.Http.Controllers{ public class HttpActionContext { public HttpActionContext(); public HttpActionContext(HttpControllerContext controllerContext, HttpActionDescriptor actionDescriptor); public Dictionary<string, object> ActionArguments { get; } public HttpActionDescriptor ActionDescriptor { get; set; } public HttpControllerContext ControllerContext { get; set; } public ModelStateDictionary ModelState { get; } public HttpRequestMessage Request { get; } public HttpResponseMessage Response { get; set; } }
}
Code 1-2 is the definition of the HttpActionContext type. The following describes the meanings of several attributes. The values in the ActionArguments attribute correspond to the parameter list of the Controller method, the Key Value is the parameter name, and the Value is the actual data Value of the parameter, because Model binding is a recursive process, this attribute is not assigned a value after the complex type of subitem is bound. Instead, the value is assigned only after all the parameter items are bound.
The ActionDescriptor attribute of the HttpActionDescriptor type. This is the second descriptive type seen after the HttpControllerDescriptor type, followed by the HttpParameterDescriptor type, here, the ActionDescriptor attribute encapsulates the information of the Controller method to be requested, similar to the metadata information of the encapsulated method.
The ControllerContext attribute does not need to be said much. Everyone knows its role. The ModelStateDictionary-type ModelState attribute will be operated only after the Model is bound, is to bind the parameter to the verified value in this attribute.
HttpActionContextExtensions
Code 1-3
Namespace System. Web. Http. Controllers {// Abstract: // contains the extension method of System. Web. Http. Controllers. HttpActionContext. [EditorBrowsable (EditorBrowsableState. never)] public static class failed {public static bool Bind (this HttpActionContext actionContext, ModelBindingContext bindingContext); public static bool Bind (this HttpActionContext actionContext, ModelBindingContext bindingContext, IEnumerable <IModelBinder> binders ); //...... }}
Code 1-3 shows the Extension Method Type HttpActionContextExtensions that contains the HttpActionContext type. here we can see two Bind () methods, which are also crucial to Model binding, because the recursion of Model binding is implemented here, we will talk about how to implement it later.
The first Bind () method here is actually to call the second Bind () method for execution. In the second Bind () method, the IEnumerable <IModelBinder> parameter obtains the current HttpControllerContext from the HttpActionContext type and obtains the configuration object HttpConfiguration object of the current request from it, finally, obtain the set of ModelBinder providers from the container properties in the configuration object, and then obtain the set of suitable types of IModelBinder Based on the ModelType using the provider set in the current ModelBindingContext, call the second Bind () method.
In this case, we may not quite understand recursion, so we should not worry about it. I will explain it later.
ModelBindingContext
Code 1-4
Namespace System. Web. Http. ModelBinding {// Abstract: // provides the context for running the model concatenation program. Public class ModelBindingContext {// Summary: // initialize a new instance of the System. Web. Http. ModelBinding. ModelBindingContext class. Public ModelBindingContext (); public ModelBindingContext (ModelBindingContext bindingContext); public bool FallbackToEmptyPrefix {get; set;} public object Model {get; set;} public ModelMetadata {get; set ;} public string ModelName {get; set;} public ModelStateDictionary ModelState {get; set;} public Type ModelType {get;} public IDictionary <string, ModelMetadata> PropertyMetadata {get ;} public ModelValidationNode ValidationNode {get; set;} public IValueProvider ValueProvider {get; set ;}}}
As shown in code 1-4, the context object is bound. First, we can see that its overload constructor has a parameter of the ModelBindingContext type to indicate nesting, the internal implementation is to pass the state value of the ModelState attribute and the ValueProvider value provider. Why is this structure? This is related to binding complex types. The structure is the same as the ModelStateDictionary type of the ModelState attribute object. This structure will be explained later.
When the Model attribute representsCurrentThe Bound Model Value in ModelBindingContext, and the attributes ModelMetadata, ModelName, ModelType, and PropertyMetadata indicateCurrentThe value of the Model in ModelBindingContext. This "current" may be of the string type or a complex type. (Complex types are encapsulated by the ASP. NET Web API framework when they are bound. This will be explained later)
Simple Type binder and binder provider
Simple Type binder- TypeConverterModelBinder
Code 1-5
public sealed class TypeConverterModelBinder : IModelBinder { // Methods public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { object obj2; ModelBindingHelper.ValidateBindingContext(bindingContext); ValueProviderResult result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (result == null) { return false; } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, result); try { obj2 = result.ConvertTo(bindingContext.ModelType); } catch (Exception exception) { if (IsFormatException(exception)) { string errorMessage = ModelBinderConfig.TypeConversionErrorMessageProvider(actionContext, bindingContext.ModelMetadata, result.AttemptedValue); if (errorMessage != null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorMessage); } } else { bindingContext.ModelState.AddModelError(bindingContext.ModelName, exception); } return false; } ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref obj2); bindingContext.Model = obj2; return true; }}
From code 1-5, we can see that the TypeConverterModelBinder type implements the IModelBinder interface, and in the BindModel () method, the ValueProvider in the binding context is directly used according to the ModelName attribute in the binding context, modelName is the combination of prefix values and attribute values described in the previous ValueProvider space. Then, the obtained result value is determined by the type. If the value cannot be correctly converted to the string type, various exceptions will be prompted. Of course, this exception will not be reported, only added to the ModelState attribute of the current binding context. If the conversion is successful, the Model value of the current binding context is assigned a value.
Simple Type binder provider- TypeConverterModelBinderProvider
Code 1-6
public sealed class TypeConverterModelBinderProvider : ModelBinderProvider { // Methods public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType) { if (modelType == null) { throw Error.ArgumentNull("modelType"); } if (!TypeHelper.HasStringConverter(modelType)) { return null; } return new TypeConverterModelBinder(); } }
The TypeConverterModelBinderProvider type shown in code 1-6 is the provider of the Simple Type binder and inherits from the ModelBinderProvider type. When it comes to this, I found that I forgot to describe this type, but it doesn't matter, let's just take a look. ModelBinderProvider is an abstract class and defines an abstract behavior.
In the implementation of the TypeConverterModelBinderProvider type, we can clearly see that if the modelType parameter can be successfully converted to the string type, the TypeConverterModelBinder type instance will be returned; otherwise, null will be returned.
Complex Type binder and complex Type binder providers
Complex encapsulation object-ComplexModelDto
Code 1-7
Namespace System. Web. Http. ModelBinding. Binders {// Abstract: // represents a data transmission object (DTO) of a complex model ). Public class metadata {public ComplexModelDto (ModelMetadata modelMetadata, IEnumerable <ModelMetadata> propertyMetadata); public ModelMetadata {get;} public Collection <ModelMetadata> PropertyMetadata {get;} public IDictionary <ModelMetadata, complexModelDtoResult> Results {get ;}}}
You can also see the comments in code 1-7, indicating a data transmission object of a complex Model, which is actually re-encapsulated for complex types, the encapsulation method is also seen in the Model metadata method. I will not talk about this type much. If you are not clear about the Model metadata, you are advised to take a look at the previous section.
MutableObjectModelBinder
I will not post too much implementation code in the MutableObjectModelBinder type. After these theoretical basics are completed, there will be code examples in the subsequent sections.
Here I will use text to describe the functions to be implemented by the MutableObjectModelBinder type. Why is MutableObjectModelBinder a complex type package? Because the MutableObjectModelBinder type does not bind, when it is executed, it can be determined that the Model of the current binding context is a complex type, at this time, MutableObjectModelBinder obtains the Model Metadata list of the attributes of the current Model based on the Properties attribute of Metadata in the current binding context, and filters the Metadata of each item based on the Model Metadata, the filtering conditions depend on the features of the application on the Model attribute, that is, the HttpBindingBehaviorAttribute type. After reading these types, let's think about them.
After obtaining the Model metadata set of the corresponding attributes under the Model, create a ComplexModelDto object instance, and create a binding context to use the new ComplexModelDto object instance as the relevant attributes of Model and ModelType, then, actionContext is called. bind (context);, that is, the HttpActionContext Extension Method in code 1-3 enters the recursion in Model binding.
Complex package providers- MutableObjectModelBinderProvider
Code 1-8
public sealed class MutableObjectModelBinderProvider : ModelBinderProvider { // Methods public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType) { if (!MutableObjectModelBinder.CanBindType(modelType)) { return null; } return new MutableObjectModelBinder(); } }
From code 1-8, you can see that the static method CanBindType () in MutableObjectModelBinder is called when determining by type. In CanBindType () method implementation, the judgment type cannot be ComplexModelDto or string, the string type is easy to understand, because it is bound to the TypeConverterModelBinder type. The ComplexModelDto type is used to prevent the processing of the Framework from entering an endless loop. You will understand it at the end.
Complex Type binder- ComplexModelDtoModelBinder
Code 1-9
public sealed class ComplexModelDtoModelBinder : IModelBinder { // Methods public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext, typeof(ComplexModelDto), false); ComplexModelDto model = (ComplexModelDto)bindingContext.Model; foreach (ModelMetadata metadata in model.PropertyMetadata) { ModelBindingContext context = new ModelBindingContext(bindingContext) { ModelMetadata = metadata, ModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName, metadata.PropertyName) }; if (actionContext.Bind(context)) { model.Results[metadata] = new ComplexModelDtoResult(context.Model, context.ValidationNode); } } return true; } }
The types of names shown in code 1-9 are not required to process the ComplexModelDto object. You can see in the code implementation that the Model in the binding context is obtained and converted to the ComplexModelDto Instance Object first, traverse the PropertyMetadata attribute, create a binding context based on the Model metadata of each item, and then call actionContext. bind (context) method, which is also one of the processes of Model binding recursion. This situation occurs when the initial Model type is complex and its attributes also contain complex types.
Complex Type binder provider- ComplexModelDtoModelBinderProvider
Code 1-10
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType) { if (modelType == null) { throw Error.ArgumentNull("modelType"); } if (!(modelType == this.ModelType)) { return null; } if (this.SuppressPrefixCheck) { return this._modelBinderFactory(); } return new SimpleModelBinder(this); }
Code 1-10 is not an implementation of the ComplexModelDtoModelBinderProvider type, but an implementation of the SimpleModelBinderProvider type. It can be divided into checking the Model prefix and not checking. You can see this by yourself.
Let's take a look at the overall structure.
Figure 1
Of course there are other ModelBinder types which will not be described here. Finally, let's take a look at the simulation of complex binding.
Figure 2
Here, the Product is a complex type, with three string-type attributes. The execution sequence is red, blue, and yellow. It should be noted that the red part will not be recursive again after entering HttpActionContextExtensions, and other blue and yellow parts are possible, as long as there is a complex type.
The Process Code Section will be posted in the example section. First, the Product type will be obtained when the Product type is bound, then, based on a series of ModelBinder providers registered in the current framework, filter and obtain the ModelBinder object that can be bound to complex types, which is also the MutableObjectModelBinder type, during MutableObjectModelBinder type processing, all the attribute Model metadata of the Product type is encapsulated as a ComplexModelDto object instance, and a ModelBindingContext1 object instance is generated for the MutableObjectModelBinder type, call the method Bind () in the extension method type HttpActionContextExtensions of the HttpActionContext type to Bind the Model. In the Bind () method, the red line flow is repeated, meaning that the process is based on Mod. The Metadata attribute in the elBindingContext1 object instance obtains the Model type. We also mentioned that the Model type is encapsulated as the ComplexModelDto type, and then the ComplexModelDtoModelBinderProvider provider is obtained by filtering based on this type, the ComplexModelDtoModelBinder instance is generated. When the ComplexModelDtoModelBinder executes the Model binding process, it traverses each attribute metadata of the ComplexModelDto type instance and generates the corresponding ModelBindingContext, in ModelBindingContext2 and ModelBindingContext3 after the binding operation is executed on ModelBindingContext2. After ModelBindingContext2 is generated, it will call the method Bind () in httpactioncontextensions of the extended method type of HttpActionContext to Bind the Model again. Because the attributes in Product are of the string type, there is no complicated type, we can see from the order below that if it is a complex type, it will be re-executed to the starting part of the red line. Because it is string type, the filtered provider type is TypeConverterModelBinderProvider, which generates a TypeConverterModelBinder instance to bind it.
Author: Jin Yuan
Source: http://www.cnblogs.com/jin-yuan/
The copyright of this article is shared by the author and the blog Park. You are welcome to reprint this article. However, you must keep this statement without the author's consent and go to the Article Page.
WEB APIs are different from web mvc APIs.
WebAPI is added to the new MVC to provide a REST-style WebService. The new WebAPI project is the same as the typical MVC project, including the main Models, Views, Controllers and other folders and Global. asax file. Views is not very useful for webapis. The Model in Models is mainly used to save the objects for interaction between the Service and the Client. By default, these objects are converted to Json format for data transmission in two rows, the Controller in Controllers is a Resource corresponding to WebService and is used to provide services. Like common MVC, Global. asax is used to configure routing rules.
For a WebAPI, ingress is initially designed as the same as that of the WCF producer client and server. Currently, the ingress we haven't mentioned yet because our request is encapsulated the HTTP request rule is used to receive responses such as AJAX and Form submission.
How can angularjs remotely call the aspnet web api?
Webapi is an interface that provides standard RESTAPI. JSON is returned by default. Therefore, angularjs accesses the asp.net web API just like accessing any http REST api Protocol.