2011-02-11 20 views
14

.NET 4 MVC 3 uygulamasında çalışıyorum. Bir alan odaklı tasarım paradigmasını takip etmeye çalışıyorum. Şu anda, uygulamam web için iki parçaya, bir alana ve MVC koduma bölünmüş durumda. Bu yapıda nerede bir RESTful web hizmeti tüketmem gerektiğini belirlemede biraz yardım isterim..NET MVC'de Tüketim REST Web Hizmeti 3

Bu özel proje, verileri almak ve sürdürmek için RESTful web hizmeti kullanmaktadır. Alanımda, aynı ada sahip web hizmetleriyle eşleşen iki "Müşteri" ve "Kullanıcı" var. Örneğin. URL/Müşteri ve URL/Kullanıcı. Her web hizmeti birkaç parametre alır ve sonra XML'de uygun bir veri listesi döndürür. Temel CRUD işlevselliğini (POST, GET, PUT ve DELETE) şeklinde uygulamam gerekiyor. Buna göre iki ana sorum var.

1.) Bu web hizmetlerini tüketmek için ne tür bir nesne oluşturmalıyım? Benim içgüdüsü içgüdüsü, CRUD işlemlerimi tanımlayan bir ICustomerService arabirimi oluşturmak ve daha sonra HTTPWebConnection (veya genişletir?) Kullanan bir sınıf biçiminde bu arabirimin bir uygulamasını oluşturmaktır. RESTful web servislerini tüketmenin daha iyi bir yolu var mı? Bu sınıf türü statik mi olmalı?

2.) Bu servis kodu nereye gitmeli? Yine, bağırıcım, kodumun Domain ve WebUI bölümlerine ek olarak, bu web hizmeti istemcilerinin arabirimlerini ve uygulamalarını içeren üçüncü bir Hizmetler bölümüne ihtiyacım olduğunu, ancak web servislerinin Müşterinin XML temsilcilerine döndüğünü söylediğini ve alanımdaki Kullanıcı varlıkları, hizmetler alandan gerçekten ayrılmayacak. peşin

sayesinde Greg

DÜZENLEME

süredir çeşitli projeler üzerinde çalıştıktan sonra, ben MVC DİNLENME web hizmetleri taşıma iyi bir yol buldum.

İlk olarak, kullanacağım çeşitli web hizmetlerini temsil eden Varlıklar oluşturuyorum. Her varlık, XML öğeleriyle özellikleri eşleştirmek için XML öznitelikleri kullanır. İşte, insanlar ve onların gömlekleri hakkında bilgi veren varsayımsal bir web hizmeti için basit bir örnek. (Bu aptalca, ama elimden gelenin en iyisini yapabilirim).

Web hizmetinden bir Kişi nesnesi aldığımı varsayalım. İşte XML.

<result> 
    <resultCount>1</resultCount> 
    <person> 
     <personName>Tom</personName> 
     <shirt> 
      <shirtColor>red</shirtColor> 
      <shirtType>sweater</shirtType> 
     </shirt> 
    </person> 
</result> 

Daha sonra iki varlığım olur: Kişi ve Gömlek. Tüm sınıfı dahil etmek istiyorum, böylece yeni başlayanlar her şeyi görebiliyorlar, bu yüzden eğer zevkleriniz için çok fazla ayrıntı varsa özür dilerim.

Kişi Daha sonra nesnelere XML ve XML nesnesini dönüştürmek için bir XmlSerializer'ı kullanabilirsiniz

using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Web; 
    using System.Xml.Serialization; 

    namespace Test.Entities 
    { 
     public class Shirt 
     { 
      [XmlElement("shirtColor")] 
      public string Color { get; set; } 

      [XmlElement("shirtType")] 
      public string Type { get; set; } 

      /* 
      This is specific to our Entity and doesn't exist in the web service so we can use 
      XmlIgnore to make the deserializer ignore it 
      */  
      [XmlIgnore] 
      public string SpecialDbId { get; set; } 
     } 

    } 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Xml.Serialization; 

namespace Test.Entities 
{ 
    [XmlRoot("person")] 
    public class Person 
    { 
     /* 
     Notice that the class name doesn't match the XML Element. This is okay because we 
     are using XmlElement to tell the deserializer that 
     Name and <personName> are the same thing 
     */ 
     [XmlElement("personName")] 
     public string Name { get; set; } 

     [XmlElement("shirt")] 
     public Shirt Shirt { get; set; } 
    } 
} 

Gömlek. İşte bunu yapmak için değiştirdiğim bir sınıf. Özgün kaynağı hatırlamadığım için özür dilerim.

ObjectSerializer

using System.Collections.Generic; 
using System.Text; 
using System.Xml; 
using System.IO; 
using System.Xml.Serialization; 
using System; 
using System.Xml.Linq; 

public static class ObjectSerializer 
{ 
    /// <summary> 
    /// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String. 
    /// </summary> 
    /// <param name="characters">Unicode Byte Array to be converted to String</param> 
    /// <returns>String converted from Unicode Byte Array</returns> 
    private static string UTF8ByteArrayToString(byte[] characters) 
    { 
     UTF8Encoding encoding = new UTF8Encoding(); 
     string constructedString = encoding.GetString(characters); 
     return (constructedString); 
    } 

    /// <summary> 
    /// Converts the String to UTF8 Byte array and is used in De serialization 
    /// </summary> 
    /// <param name="pXmlString"></param> 
    /// <returns></returns> 
    private static Byte[] StringToUTF8ByteArray(string pXmlString) 
    { 
     UTF8Encoding encoding = new UTF8Encoding(); 
     byte[] byteArray = encoding.GetBytes(pXmlString); 
     return byteArray; 
    } 

    /// <summary> 
    /// Serialize an object into an XML string 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="obj"></param> 
    /// <returns></returns> 
    public static string SerializeObject<T>(T obj) 
    { 
     try 
     { 
      XDocument xml; 
      using (MemoryStream stream = new MemoryStream()) 
      { 
       XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
       ns.Add("", ""); 
       XmlSerializer serializer = new XmlSerializer(typeof(T)); 
       serializer.Serialize(stream, obj, ns); 
       stream.Close(); 
       byte[] buffer = stream.ToArray(); 
       UTF8Encoding encoding = new UTF8Encoding(); 
       string stringXml = encoding.GetString(buffer); 
       xml = XDocument.Parse(stringXml); 
       xml.Declaration = null; 
       return xml.ToString(); 
      } 

     } 
     catch 
     { 
      return string.Empty; 
     } 
    } 

    /// <summary> 
    /// Reconstruct an object from an XML string 
    /// </summary> 
    /// <param name="xml"></param> 
    /// <returns></returns> 
    public static T DeserializeObject<T>(string xml) 
    { 
     XmlSerializer xs = new XmlSerializer(typeof(T)); 
     MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xml)); 
     XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
     return (T)xs.Deserialize(memoryStream); 
    } 
} 

HTTP işlemleri işlemek için genel bir hizmet oluşturmak, sonra (bu sınıfın içinde iyileştirilmesi için oda bir sürü muhtemelen vardır). GET ve POST kullanıyorum. İşte benim sınıfım. HTTPService

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Net.Security; 
using System.Security.Cryptography.X509Certificates; 
using System.Text; 
using System.Web; 
using System.Web.Mvc; 
using System.Xml.Linq; 

namespace Test.Infrastructure 
{ 
    public class HttpService 
    { 
     public HttpService() 
     { 
      ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(AcceptCertificate); 
     } 

     public XDocument Post(Uri host, string path, Dictionary<string, string> headers, string payload, NetworkCredential credential) 
     { 
      try 
      { 
       Uri url = new Uri(host.Url, path); 
       MvcHtmlString encodedPayload = MvcHtmlString.Create(payload); 
       UTF8Encoding encoding = new UTF8Encoding(); 
       byte[] data = encoding.GetBytes(encodedPayload.ToHtmlString()); 

       HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
       request.Method = "POST"; 
       request.Credentials = credential; 
       request.ContentLength = data.Length; 
       request.KeepAlive = false; 
       request.ContentType = "application/xml"; 

       MvcHtmlString htmlString1; 
       MvcHtmlString htmlString2; 
       foreach (KeyValuePair<string, string> header in headers) 
       { 
        htmlString1 = MvcHtmlString.Create(header.Key); 
        htmlString2 = MvcHtmlString.Create(header.Value); 
        request.Headers.Add(htmlString1.ToHtmlString(), htmlString2.ToHtmlString()); 
       } 

       using (Stream requestStream = request.GetRequestStream()) 
       { 
        requestStream.Write(data, 0, data.Length); 
        requestStream.Close(); 
       } 

       using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
       using (Stream responseStream = response.GetResponseStream()) 
       { 
        if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created) 
        { 
         throw new HttpException((int)response.StatusCode, response.StatusDescription); 
        } 

        XDocument xmlDoc = XDocument.Load(responseStream); 
        responseStream.Close(); 
        response.Close(); 

        return xmlDoc; 
       } 
      } 
      catch (Exception ex) 
      { 
       throw; 
      } 
     } 

     public XDocument Get(Uri host, string path, Dictionary<string, string> parameters, NetworkCredential credential) 
     { 
      try 
      { 
       Uri url; 
       StringBuilder parameterString = new StringBuilder(); 

       if (parameters == null || parameters.Count <= 0) 
       { 
        parameterString.Clear(); 
       } else { 
        parameterString.Append("?"); 
        foreach (KeyValuePair<string, string> parameter in parameters) 
        { 
         parameterString.Append(parameter.Key + "=" + parameter.Value + "&"); 
        } 
       } 
       url = new Uri(host.Url, path + parameterString.ToString().TrimEnd(new char[] { '&' })); 

       HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
       request.Credentials = credential; 
       using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
       { 
        if (response.StatusCode != HttpStatusCode.OK) 
        { 
         throw new HttpException((int)response.StatusCode, response.StatusDescription); 
        } 

        XDocument xmlDoc = XDocument.Load(response.GetResponseStream()); 
        return xmlDoc; 

       } 
      } 
      catch (Exception ex) 
      { 
       throw; 
      } 
     } 


     /* 
     I use this class for internal web services. For external web services, you'll want 
     to put some logic in here to determine whether or not you should accept a certificate 
     or not if the domain name in the cert doesn't match the url you are accessing. 
     */ 
     private static bool AcceptCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 
     { 
      return true; 
     } 

    } 
} 

Sonra HTTPService kullanmak için kaynak yaratmaktır. İnsanları bir web hizmeti sorgusundan döndürecek kolay bir GetPeople() yöntemini uyguladı.

Depo

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Data.Linq; 
using System.Configuration; 
using Test.Entities; 

namespace Test.Infrastructure 
{ 
    public class PersonRepository 
    { 
     private HttpService _httpService; 

     public PersonRepository() 
     { 
      _httpService = new HttpService(); 
     } 

     public IQueryable<Person> GetPeople() 
     { 
      try 
      { 
       Uri host = new Uri("http://www.yourdomain.com"); 
       string path = "your/rest/path"; 
       Dictionary<string, string> parameters = new Dictionary<string, string>(); 

       //Best not to store this in your class 
       NetworkCredential credential = new NetworkCredential("username", "password"); 

       XDocument xml = _httpService.Get(host, path, parameters, credential); 
       return ConvertPersonXmlToList(xml).AsQueryable(); 

      } 
      catch 
      { 
       throw; 
      } 
     } 

     private List<Person> ConvertPersonXmlToList(XDocument xml) 
     { 
      try 
      { 
       List<Person> perople = new List<Person>(); 
       var query = xml.Descendants("Person") 
           .Select(node => node.ToString(SaveOptions.DisableFormatting)); 
       foreach (var personXml in query) 
       { 
        people.Add(ObjectSerializer.DeserializeObject<Person>(personXml)); 
       } 
       return people; 
      } 
      catch 
      { 
       throw; 
      } 

     } 
    } 
} 

Son olarak, kumanda içinde depo kullanmak gerekir. Burada bağımlılık enjeksiyonu kullanmıyorum (DI), ama son yapınızda ideal olarak yapmak istersiniz.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 
using Test.Entities; 
using Test.Infrastructure; 
using System.Net; 
using System.Text; 

namespace Test.Controllers 
{ 
    public class PeopleController 
    { 
     private PersonRepository _personRepository; 

     public PeopleController() 
     { 
      _personRepository = new PersonRepository(); 
     } 

     public List<Person> List() 
     { 
      return _personRepository.GetPeople().ToList<Person>(); 
     } 
    } 
} 

Denetleyici ben anında bu daktilo ve benim gerçek çözümünden modifiye, bu yüzden herhangi bir yazım hataları veya hatalar için özür dileriz. Bulduğum her şeyi düzeltmek için elimden gelenin en iyisini yapacağım, ancak bu REST tabanlı web servisleri ile başa çıkmak için yeniden kullanılabilir bir çözüm yaratmaya iyi bir başlangıç ​​yapmalıdır.

cevap

4

Sen doğru yoldasın. ICustomerService'i etki alanı paketine ve bu hizmetin bir HttpWebConnection uygulamasını etki alanı paketine başvuran ayrı bir pakette yerleştiririm.

Bu sınıf statik olabilir ancak olmak zorunda değildir - sen şüphe iseniz, o zaman statik yapmazlar.

Sen hizmetleri tamamen etki alanından ayrılmış olmadığını haklısın ama onlar etki bakımından alanı tabakasında tanımlanan bir hizmet sözleşmesi uygulamak olmalarındandır. etki ayrılmasıdır Ne onlar sabun/webservice istemcileri veya http/dinlenme müşterilerine konum ve bu Etki alanı kodunda istemiyoruz teknik ayrıntıları olduğu bir gerçektir.

Yani servis uygulaması alan varlıklar içine XML çevirir ve etki alanındaki diğer nesnelere kullanılabilir hale getirir. Bunun için

+0

Teşekkür tükendiğinde - bu çok açıktı. Arabirimi ve uygulamayı ayırmak mantıklıdır çünkü o zaman etki alanım, verinin nereden geldiğini umursamamaktadır. Daha sonra, başka bir kodu değiştirmeden, veritabanına doğrudan bir bağlantı ile aynı hizmeti vermeme izin vermektedir. – GregB

+0

Diyelim ki, bir kaç eylemi olan bir Liste/Denetleyici var, ListCustomers, ListCustomer (string id), UpdateCustomer ve DeleteCustomer. Bu eylemler/görünümler için modeller oluşturduğumda, bu modeller sadece bir Liste veya tek bir Müşteri olacak mı? Contexts ve Agrega'ları okudum, ancak bunları uygulamak için somut bir şey bulamadım. Yine, benim bağırsak reaksiyonum, varlığımı içeren bir modele sahip olacağımı ve denetleyicinin modelleri güncellemek için uygun hizmeti arayacağını ve sonra modeli tekrar görüntüye döndüreceğimi, ancak bir çaylak olduğumu, bu yüzden uzak durun. – GregB

+0

Bazı insanlar alan adı nesnelerini bir mvc uygulamasında model olarak kullanırlar, bu basit durumlarda tamamdır. Yaklaşımınız daha iyi IMO: asp.net mvc modellerinde etki alanı nesneleri ve hizmetleri sarın.Bir an için bağlamları ve toplamları düşünmeyi bırakıp asp.net mvc'deki modellere göz atmak isteyebilirsiniz. – Marijn

İlgili konular