2013-03-20 176 views
13

我正在尋找處理WebApi中的繼承類型的模型綁定,我真正想要做的是處理綁定使用默認模型綁定(除了選擇類型的地方它無法這樣做),但我錯過了一些基本的東西。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調用?

+0

您是否找到了解決辦法? – iuristona 2015-03-10 19:31:38

回答

3

以下是我在我的類型中繼承的例子,並且在Json格式化程序的一些設置(如使用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; 
     } 
    } 
} 
+0

欣賞它Kiran,這絕對是有用的代碼。最後,雖然不是你使用XML和JSON序列化,而不是模型聯編程序? (通過類型信息,我不幸無法添加Json,以及通過DataContract屬性的xml)。我真的想使用模型綁定器,因爲我最終將無法修飾所有類型,也不能需要JSON的類型信息。 – Gene 2013-03-20 23:46:36