我正在寻找在WebApi中继承类型的模型绑定,而我真正想要做的是使用默认模型绑定来处理绑定(除了选择不能这样做的类型),但是我遗漏一些根本的东西
所以说我有类型:
- public abstract class ModuleVM
- {
- public abstract ModuleType ModuleType { get; }
- }
- public class ConcreteVM : ModuleVM
- {
- }
使用MVC控制器,我会这样做:
- public class ModuleMvcBinder : DefaultModelBinder
- {
- protected override object CreateModel(ControllerContext controllerContext,ModelBindingContext bindingContext,Type modelType)
- {
- if (modelType == typeof(ModuleVM))
- {
- // Just hardcoding the type for simplicity
- Type instantiationType = typeof(ConcreteVM);
- var obj = Activator.CreateInstance(instantiationType);
- bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null,instantiationType);
- bindingContext.ModelMetadata.Model = obj;
- return obj;
- }
- return base.CreateModel(controllerContext,bindingContext,modelType);
- }
- }
- [AttributeUsage( AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Struct | AttributeTargets.Property,AllowMultiple = false,Inherited = false)]
- public class ModuleMvcBinderAttribute : CustomModelBinderAttribute
- {
- public override IModelBinder GetBinder()
- {
- return new ModuleMvcBinder();
- }
- }
然后使用控制器上的属性,一切都很好,我正在利用DefaultModelBinder进行实际工作,我基本上只是提供正确的对象实例化.
那么如何为WebApi版本做同样的呢?
如果我使用自定义模型绑定器(例如Error implementing a Custom Model Binder in Asp.Net Web API),我的问题是(我相信)在BindModel方法中,我没有找到在实例化对象之后使用“标准”http绑定的好方法.我可以专门针对其他帖子中提到的JSON(Deserialising Json to derived types in Asp.Net Web API)或XML(Getting my Custom Model bound to my POST controller)进行处理,但在我看来,这是因为web api应该分离出来,而是 – 它只是不知道如何确定方式. (所有具体类型自然处理得很好.)
我是否忽略了一些明显的事情,我应该在实例化对象后引导BindModel调用?
解决方法
以下是我在我的类型中继承的一个示例,并且在某些设置(如使用KnownType属性装饰,Xml格式化程序的datacontractserializer需要)和TypeNameHandling设置之后,我们可以期待两个xml / json请求的一致行为.
- using Newtonsoft.Json;
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Formatting;
- using System.Net.Http.Headers;
- using System.Runtime.Serialization;
- using System.Web.Http;
- using System.Web.Http.SelfHost;
- namespace Service
- {
- class Service
- {
- private static HttpSelfHostServer server = null;
- private static string baseAddress = string.Format("http://{0}:9095/",Environment.MachineName);
- static void Main(string[] args)
- {
- HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
- config.Routes.MapHttpRoute("Default","api/{controller}/{id}",new { id = RouteParameter.Optional });
- config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
- config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
- try
- {
- server = new HttpSelfHostServer(config);
- server.OpenAsync().Wait();
- Console.WriteLine("Service listenting at: {0} ...",baseAddress);
- TestWithHttpClient("application/xml");
- TestWithHttpClient("application/json");
- Console.ReadLine();
- }
- catch (Exception ex)
- {
- Console.WriteLine("Exception Details:\n{0}",ex.ToString());
- }
- finally
- {
- if (server != null)
- {
- server.CloseAsync().Wait();
- }
- }
- }
- private static void TestWithHttpClient(string mediaType)
- {
- HttpClient client = new HttpClient();
- MediaTypeFormatter formatter = null;
- // NOTE: following any settings on the following formatters should match
- // to the settings that the service's formatters have.
- if (mediaType == "application/xml")
- {
- formatter = new XmlMediaTypeFormatter();
- }
- else if (mediaType == "application/json")
- {
- JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
- jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
- formatter = jsonFormatter;
- }
- HttpRequestMessage request = new HttpRequestMessage();
- request.RequestUri = new Uri(baseAddress + "api/students");
- request.Method = HttpMethod.Get;
- request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
- HttpResponseMessage response = client.SendAsync(request).Result;
- Student std = response.Content.ReadAsAsync<Student>().Result;
- Console.WriteLine("GET data in '{0}' format",mediaType);
- if (StudentsController.CONSTANT_STUDENT.Equals(std))
- {
- Console.WriteLine("both are equal");
- }
- client = new HttpClient();
- request = new HttpRequestMessage();
- request.RequestUri = new Uri(baseAddress + "api/students");
- request.Method = HttpMethod.Post;
- request.Content = new ObjectContent<Person>(StudentsController.CONSTANT_STUDENT,formatter);
- request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
- Student std1 = client.SendAsync(request).Result.Content.ReadAsAsync<Student>().Result;
- Console.WriteLine("POST and receive data in '{0}' format",mediaType);
- if (StudentsController.CONSTANT_STUDENT.Equals(std1))
- {
- Console.WriteLine("both are equal");
- }
- }
- }
- public class StudentsController : ApiController
- {
- public static readonly Student CONSTANT_STUDENT = new Student() { Id = 1,Name = "John",EnrolledCourses = new List<string>() { "maths","physics" } };
- public Person Get()
- {
- return CONSTANT_STUDENT;
- }
- // NOTE: specifying FromBody here is not required. By default complextypes are bound
- // by formatters which read the body
- public Person Post([FromBody] Person person)
- {
- if (!ModelState.IsValid)
- {
- throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest,this.ModelState));
- }
- return person;
- }
- }
- [DataContract]
- [KnownType(typeof(Student))]
- public abstract class Person : IEquatable<Person>
- {
- [DataMember]
- public int Id { get; set; }
- [DataMember]
- public string Name { get; set; }
- public bool Equals(Person other)
- {
- if (other == null)
- return false;
- if (ReferenceEquals(this,other))
- return true;
- if (this.Id != other.Id)
- return false;
- if (this.Name != other.Name)
- return false;
- return true;
- }
- }
- [DataContract]
- public class Student : Person,IEquatable<Student>
- {
- [DataMember]
- public List<string> EnrolledCourses { get; set; }
- public bool Equals(Student other)
- {
- if (!base.Equals(other))
- {
- return false;
- }
- if (this.EnrolledCourses == null && other.EnrolledCourses == null)
- {
- return true;
- }
- if ((this.EnrolledCourses == null && other.EnrolledCourses != null) ||
- (this.EnrolledCourses != null && other.EnrolledCourses == null))
- return false;
- if (this.EnrolledCourses.Count != other.EnrolledCourses.Count)
- return false;
- for (int i = 0; i < this.EnrolledCourses.Count; i++)
- {
- if (this.EnrolledCourses[i] != other.EnrolledCourses[i])
- return false;
- }
- return true;
- }
- }
- }