在.NET中使用REST Web服务MVC 3 [英] Consuming REST Web Service in .NET MVC 3

查看:120
本文介绍了在.NET中使用REST Web服务MVC 3的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个.NET 4 MVC 3应用程序。我试图遵循领域驱动设计范例。现在,我的应用程序被分为两部分,一个领域,我的MVC code代表网页。我想一些帮助确定在哪里这个结构我都要消耗一个RESTful Web服务。

I am working on a .NET 4 MVC 3 application. I'm trying to follow a domain driven design paradigm. Right now, my application is broken into two pieces, a domain and my MVC code for the web. I'd like some help in determining where in this structure I should consume a RESTful web service.

此特定项目使用RESTful Web服务来检索和持久化数据。在我的领域,我有两个实体客户和用户,这对与具有相同名称的网络服务。例如URL /客户和URL /用户。每个web服务需要几个参数,然后返回XML数据的适当列表。我需要实现的形式基本的CRUD功能(POST,GET,PUT和DELETE)。鉴于此,我有两个主要的问题。

This specific project uses a RESTful web service to retrieve and persist data. In my domain, I have two entities "Customer" and "User" which pair up with web services of the same name. e.g. URL/Customer and URL/User. Each web services takes a few parameters and then returns an appropriate list of data in XML. I need to implement basic CRUD functionality in the form of (POST, GET, PUT and DELETE). Given this, I have two main questions.

1)我应该创建什么类型的对象,消费这些Web服务?我的直觉是创建一个ICustomerService接口,它定义我的CRUD操作,然后创建了一个接口实现在使用HTTPWebConnection一类的形式(或扩展吗?)。有没有更好的方式来消费REST Web服务?如果这种类型的类是静态的?

1.) What type of object should I create to consume these web services? My gut instinct is to create an ICustomerService interface that defines my CRUD operations, and then create an implementation of that interface in the form of a class that uses HTTPWebConnection (or extends it?). Is there a better way to consume RESTful web services? Should this type of class be static?

2。)应该在哪里这个服务code去了?同样,我的直觉告诉我,除了我的code域与WebUI部分,我需要的是包含这些Web服务客户端的接口和实现第三,服务部分,但是由于Web服务返回的XML重新这是我的域的客户和用户的实体presentations,该服务将无法真正去耦从域。

2.) Where should this service code go? Again, my gut tells me that in addition to the Domain and WebUI sections of my code, I need a third, Services section that contains the interfaces and implementations of these web service clients, but since the web services are returning XML representations of the Customer and User entities that are in my domain, the services won't really be de-coupled from the domain.

在此先感谢, 格雷格

修改

在工作的各个项目一段时间后,我发现的MVC的处理REST Web服务的一个很好的方式。

After working on various projects for some time, I've found a good way of handling REST web services in MVC.

首先,我创建重新present,我将使用各种Web服务的实体。每个实体使用XML属性相匹配的属性的XML元素。这里有一个简单的例子,它返回有关人民和他们的衬衣(这是愚蠢的,但最好我能飞上来)。

Firstly, I create Entities that represent the various web services that I will be using. Each entity uses XML attributes to match up the properties with the XML elements. Here's a simple example for a hypothetical web service that returns information about people and their shirts (it's stupid, but the best I could come up with on the fly).

比方说,我收到来自Web服务的Person对象。这里的XML。

Let's say I'm getting a Person object from the web service. Here's the XML.

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

我会再有两个实体:个人和衬衫。我想包括整个类,以便新手所看到的一切,所以我很抱歉,如果这是太详细为您的口味。

I would then have two entities: Person and Shirt. I like to include the entire class so that newbies can see everything, so I'm sorry if this is too verbose for your tastes.

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对象。这里是一个I类改性做到这一点。我很抱歉,我不记得原来的来源。 (有可能是一个很大的改进空间这个类中)

We can then use a XmlSerializer to convert object to XML and XML to objects. Here is a class I modified to do this. I apologize as I don't remember the original source. (There is probably a lot of room for improvement within this class)

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。这是我的课。

Then, create a generic service to handle your HTTP operations. I use GET and POST. Here is my class.

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()方法,将返回的人从Web服务查询。

Then you create your repository to use the HttpService. I implemented an easy GetPeople() method that would return people from a web service query.

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),但你最好要在你的最终版本。

Finally, you need to use your repository in your controller. I'm not using any dependency injection here (DI), but you'd ideally want to in your final build.

控制器

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服务。

I typed this up on the fly and modified it from my actual solution, so I apologize for any typos or errors. I'll do my best to correct anything that I find, but this should give a good start to creating a re-usable solution for dealing with REST-based web services.

推荐答案

你是在正确的轨道。我将ICustomerService在域包,并在一个单独的包引用域包的HttpWebConnection实现这项服务。

You're on the right track. I would place the ICustomerService in the domain package, and an HttpWebConnection implementation of this service in a separate package that references the domain package.

本类可以是静态的,但并不一定是 - 如果你有疑问,那么就不要让它静止。

This class can be static, but does not have to be - if you're in doubt, then don't make it static.

您说的对的服务没有完全从域中分离,但那是因为它们实现的领域层中定义的服务合约,在域中的条款。  什么是从域中分离的事实是,他们是SOAP / Web服务客户端或HTTP / REST客户端,而这些都是你不想要在你的域code中的技术细节。

You're right that the services are not completely decoupled from the domain, but that is because they implement a service contract defined in the domain layer, in terms of the domain. What is decoupled from the domain is the fact that they're soap/webservice clients or http/rest clients and those are the technical details that you don't want in your domain code.

所以,你的服务实现XML转换成域实体,使他们能够在域中的其他对象可用。

So your service implementation translates XML into domain entities and makes them available to the other objects in the domain.

这篇关于在.NET中使用REST Web服务MVC 3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆