Я работаю над приложением .NET 4 MVC 3. Я пытаюсь следовать парадигме проекта, управляемой доменом. Сейчас мое приложение разбито на две части: домен и мой код MVC для Интернета. Мне нужна помощь в определении того, где в этой структуре я должен использовать веб-службу RESTful.
В этом конкретном проекте используется веб-служба RESTful для извлечения и сохранения данных. В моем домене у меня есть два объекта "Клиент" и "Пользователь", которые соединяются с веб-службами с тем же именем. например URL/Клиент и URL/Пользователь. Каждая веб-служба принимает несколько параметров, а затем возвращает соответствующий список данных в XML. Мне нужно реализовать базовые функции CRUD в виде (POST, GET, PUT и DELETE). Учитывая это, у меня есть два основных вопроса.
1.) Какой тип объекта я должен создать для использования этих веб-сервисов? Мой инстинкт кишки заключается в создании интерфейса ICustomerService, который определяет мои операции CRUD, а затем создает реализацию этого интерфейса в виде класса, который использует HTTPWebConnection (или расширяет его?). Есть ли лучший способ использовать веб-службы RESTful? Если этот тип класса статичен?
2.) Куда должен идти этот служебный код? Опять же, моя кишка говорит мне, что в дополнение к разделам Domain и WebUI моего кода мне нужен третий раздел "Службы", который содержит интерфейсы и реализации этих клиентов веб-служб, но поскольку веб-службы возвращают XML-представления Клиента и пользовательские объекты, которые находятся в моем домене, службы на самом деле не будут отменены из домена.
Спасибо заранее, Грег
ИЗМЕНИТЬ
После нескольких проектов в течение некоторого времени я нашел хороший способ обработки веб-сервисов REST в MVC.
Во-первых, я создаю объекты, которые представляют различные веб-службы, которые я буду использовать. Каждый объект использует атрибуты XML для сопоставления свойств с элементами XML. Вот простой пример гипотетической веб-службы, которая возвращает информацию о людях и их рубашках (это глупо, но лучше всего я могу придумать на лету).
Скажем, я получаю объект Person из веб-службы. Здесь XML.
<result>
    <resultCount>1</resultCount>
    <person>
        <personName>Tom</personName>
        <shirt>
            <shirtColor>red</shirtColor>
            <shirtType>sweater</shirtType>
        </shirt>
    </person>
</result>
Тогда у меня было бы два объекта: Person and Shirt. Мне нравится включать весь класс, чтобы новички могли видеть все, поэтому я сожалею, если это слишком многословно для ваших вкусов.
Person
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, 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;
        }
    }
}
Затем вы создаете свой репозиторий для использования HttpService. Я реализовал простой метод GetPeople(), который возвращал бы людей из запроса веб-службы.
Repository
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.
