2013-03-20 15 views
13

WebApiで継承された型のモデルバインディングを処理しようとしています。実際に探しているのは、デフォルトのモデルバインディングを使用してバインディングを処理することですそうすることはできませんが)、私は何か基本的なものを欠いています。WebApi継承型のモデルバインド

だから私はタイプを持っていると言う:、私はこのようなものだろうMVCコントローラを使用して

public abstract class ModuleVM 
{ 
    public abstract ModuleType ModuleType { get; } 
} 

public class ConcreteVM : ModuleVM 
{ 

} 

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

答えて

3

以下は、私が自分の型で継承し、いくつかの設定(Xmlフォーマッタのdatacontractserializerで必要なKnownType属性で装飾するなど)とJsonフォーマッタでの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のシリアル化を使用していませんか? (私は残念ながら追加できない型情報、およびDataContract属性を介したXMLを使ってJsonを使っています)。私は最終的にすべての型を飾ることができないのでモデルバインダーを使用したいと思います。 JSONの型情報が必要です。 – Gene