该特定项目使用RESTful Web服务来检索和保留数据.在我的域中,我有两个实体“客户”和“用户”,与同名的Web服务配对.例如网址/客户和网址/用户.每个Web服务都需要一些参数,然后以XML格式返回适当的数据列表.我需要以(POST,GET,PUT和DELETE)的形式实现基本的CRUD功能.鉴于此,我有两个主要问题.
1.我应该创建什么类型的对象来使用这些Web服务?我的直觉是创建一个定义我的CRUD操作的ICustomerService接口,然后以使用HTTPWebConnection(或扩展它)的类的形式创建该接口的实现.有没有更好的方式来消费RESTful Web服务?这种类应该是静态的吗?
2.)这个服务代码应该在哪里?我的直觉告诉我,除了我的代码的Domain和WebUI部分,我需要第三个Services部分,其中包含这些Web服务客户端的接口和实现,但是由于Web服务返回客户的XML表示和我的域中的用户实体,服务将不会真正地从域中解除耦合.
提前致谢,
格雷格
编辑
在多个项目工作了一段时间之后,我发现了一种在MVC中处理REST Web服务的好方法.
首先,我创建代表我将要使用的各种Web服务的实体.每个实体使用XML属性来匹配XML元素的属性.这是一个简单的示例,用于返回有关人员和衬衫的信息(这是愚蠢的,但是我能够快速提出).
假设我从Web服务获取一个Person对象.这是XML.
<result> <resultCount>1</resultCount> <person> <personName>Tom</personName> <shirt> <shirtColor>red</shirtColor> <shirtType>sweater</shirtType> </shirt> </person> </result>
我会有两个实体:人和衬衫.我喜欢包括整个班级,以便新手可以看到所有的东西,所以我很抱歉,如果这对你的口味来说太冗长了.
人
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; } } }
衬衫
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; } } }
然后,我们可以使用XmlSerializer将对象转换为XML和XML到对象.这是我修改过的一个类.我很抱歉,因为我不记得原来的来源. (这个班级内可能还有很多改进的余地)
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操作.我使用GET和POST.这是我的课
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,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> 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; } } }
然后创建您的存储库以使用HttpService.我实现了一个简单的GetPeople()方法,可以从Web服务查询返回人员.
知识库
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; } } } }
最后,您需要在控制器中使用您的存储库.我没有在这里使用任何依赖注入(DI),但你最好要在最后的版本中.
调节器
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>(); } } }
我在飞行中输入了这个,并将其从我的实际解决方案中进行了修改,所以我为任何打字错误或者错误道歉.我会尽力纠正我发现的任何东西,但这应该是一个很好的开始,创建一个可用的解决方案来处理基于REST的Web服务.
解决方法
这个类可以是静态的,但不一定是 – 如果你有疑问,那么不要使它静态.
您是对的,服务不是完全从域解耦,而是因为它们实现了在领域层定义的服务契约.
与域分离的是它们是soap / webservice客户端或http / rest客户端,而这些是您不需要在域代码中的技术细节.
因此,您的服务实现将XML转换为域实体,并使其可用于域中的其他对象.