一、理解 REST 服务
具象状态传输(REST)是一种架构风格,用于创建可扩展的服务。 基于 REST 的 Web 服务是符合 REST 架构约束的 Web 服务。 REST 架构风格在设计和架构能够通信的应用方面很快在世界范围内变得非常流行。 由于其简单性,它取代了基于 SOAP 和 wsdl 的 Web 服务,在世界范围内得到了广泛的接受。 它本质上是一个客户机-服务器架构,并使用无状态 HTTP 协议。 在本书中,我们将介绍使用 HTTP 协议的 REST。 我们掌握 REST 和 Web API 的旅程才刚刚开始!
在本章中,我们将涵盖以下主题:
- 休息
- 资源和 URI
- 休息和 RPC
- 在。net 4.5 中实现 RESTful 服务
- 创建 WCF 服务
- 使 WCF 服务 RESTful
- 指定绑定信息
- 托管服务
- 返回 JSON 数据
- 使用 RESTful 服务
理解 REST
其他是什么? 为什么随着时间的推移,它变得如此受欢迎? REST 是 Web 服务的替代品吗? 如何利用。net 框架来实现 RESTful 服务? 我们将在本章的章节中回答这些问题。
REST 是一种用于设计能够相互通信的分布式应用的架构风格。 请注意,REST 不是一种技术或一组标准。 相反,它是一组约束,可以用来定义一种新的架构风格。 本质上,它是一种客户机-服务器架构风格,其中连接是无状态的。
注意事项
请注意,REST 架构样式也可以应用于其他协议。 “无状态”一词意味着 HTTP/HTTPS 协议。 REST 架构风格在 HTTP 世界中很流行,当与 HTTP 协议结合使用时,可以得到更好的结果。
REST 不是标准; 相反,它是 RPC 和 Web 服务的架构替代方案。 在 REST 架构风格中,您可以使用 HTTP 协议(如果 HTTP 是正在使用的协议)在系统之间进行通信。 实际上,万维网(WWW)可以看作是一个基于 rest 的架构。 RESTful 架构基于可缓存和无状态的通信协议。
REST 是一种架构风格,它将应用的状态和功能划分为资源。 这些资源又可以通过 HTTP 上的 uri 进行寻址。 这些资源有一个公共接口,并且是唯一可寻址的。 基于 rest 的模型是无状态的、基于客户机-服务器的和可缓存的。
如前所述,在基于 rest 的模型中,资源用于表示状态和功能。 资源是通过逻辑 url 标识的。 在典型的基于 rest 的模型中,客户机和服务器使用请求和响应进行通信。 客户端向服务器发送资源请求,然后服务器将响应返回给客户端。
REST 架构风格的主要设计目标包括:
- 组件独立部署
- 减少延迟
- 服务交互的高安全性
- 可伸缩性
- 高性能
SOAP 和 REST 之间的基本区别在于前者强调动词,而后者强调资源。 在 REST 中,定义资源,然后使用统一的接口使用 HTTP 谓词对资源进行操作。 还应该注意的是,REST 使用起来更简单,因为它充分利用 HTTP 传输机制来格式化、缓存、路由和对给定资源执行操作。 相反,对于 SOAP,没有这样的约定。 基于 soap 的服务可以很容易地通过 TCP/IP、UDP、SMTP 或任何其他传输协议公开。 所以,它不必依赖于 HTTP 协议。
在基于 rest 的模型中,请求由端点 URL、开发人员 ID、参数和所需的操作组成。 端点 URL 用于表示完整的地址。 开发人员 ID 是唯一标识每个请求来源的键。 期望的动作用来表示要执行的动作。
REST 架构为CRUD(创建、读取、更新和删除)操作使用了一些常见的 HTTP 方法。 这些措施如下:
GET
:用于请求资源的特定表示。HEAD
:此用于只检索资源报头。PUT
:是用于更新资源。DELETE
:用于删除指定的资源。POST
:用于提交将由标识的资源处理的数据。 理想情况下,POST
应该仅用于创建资源,而PUT
仅用于更新资源。
基于 rest 架构的资源
资源概念是 REST 中最重要的概念之一。 REST 的一些公共实现示例包括:
- 谷歌融合表
- Sones GraphDB:一个用 c#编写的面向图形的数据库
- Nuxeo:一个开源文档管理器
使用 URI 标识资源。 在 REST 风格的架构中,服务器和客户机之间的通信使用请求和响应进行。 客户端(也称为消费者)从服务器请求资源。 然后,服务器将响应发送回客户机。
在 REST 架构范例中,资源用于表示资源的状态和功能。 通过使用逻辑 uri 来标识它们,这样它们就可以被普遍寻址。 REST 架构本质上基于 http——一种无状态协议。 然而,资源可以在需要时进行缓存。 注意,因为 HTTP 提供了缓存机制,所以在 HTTP 协议上实现的 REST 提供了 HTTP 的特性和好处。 此外,还可以为缓存的数据设置缓存过期策略。
任何 REST 请求都包含以下组件:
- 端点 URL:这个表示脚本的完整地址。
- 开发人员 ID:这是一个与每个请求一起发送的键。 这用于标识请求的来源。 注意,开发人员 ID 不是所有 REST 服务都需要的。
- Parameters:这表示请求的参数。 这是可选的。
- 期望操作:这表示特定请求的操作。 操作基于 HTTP 动词。
让我们举个例子。 以下链接是一个典型的 REST 请求 URL:http://localhost/payroll?devkey=1&action=search&type=department&keyword=DepartmentID。
在前面的请求中,端点是http://localhost/payroll
,期望的操作是search
,开发人员键是1
。 您还可以在请求中提供type
和keyword
参数。 请参考下面的代码片段,它展示了 REST 请求和响应的样子:
<?xml version="1.0" encoding=" UTF-8"?>
<Request>
<RequestId>1R3ABC</RequestId>
<Parameters>
<Argument Name="devkey" Value="1" />
<Argument Name="action" Value="search" />
<Argument Name="type" Value="department" />
<Argument Name="keyword" Value="phone" />
</Parameters>
</Request>
<Response>
<ResultCount>2</ResultCount>
<Record>
<FirstName>Joe</FirstName>
<LastName>Stagner</LastName>
<DepartmentID>1</DepartmentID>
</Record>
<Record>
<FirstName>Stephen</FirstName>
<LastName>Smith</LastName>
<DepartmentID>1</DepartmentID>
</Record>
</Response>
REST 架构约束
REST 架构范例对架构定义了以下约束:
客户机-服务器
基于的 rest 实现基于客户机-服务器模型。 服务器和客户机是明显隔离的。 这意味着可以独立地修改服务器和客户机。 服务器完全不关心用户界面。 类似地,用户界面并不关心数据是如何持久化的。
无国籍
REST 架构基于无状态 HTTP 协议。 在 RESTful 架构中,客户机可以缓存服务器响应。 从客户机到服务器的任何请求都应该有足够的信息,以便能够理解和服务请求,但不会在服务器中存储客户机上下文。 这种类型的设计确保服务器对性能监视更加可见,并且具有可伸缩性。
可缓存
在典型的 REST 架构中,客户机应该能够缓存数据。 为了更好地管理缓存,该架构允许我们设置是否可以缓存响应。 该特性提高了可伸缩性和性能。
代码随需应变
REST 架构中的服务器可以(如果需要)扩展或定制特定客户机的功能。 这就是所谓的“随需应变代码”; 该特性允许 REST 架构实现中的服务器在需要时将逻辑传输到客户机。
统一接口
REST 架构风格在客户端和服务器之间定义了统一的接口; 因此,它只允许使用标准 HTTP 动词定义的一组有限的操作,例如GET
、PUT
、POST
和DELETE
。
资源管理
资源是 REST 风格架构中最重要的概念。 使用惟一的 uri 标识资源。 请注意,资源表示可以以任何数字格式(HTML、XML、JSON、RSS 等)的任何组合形式存在。
注意事项
这里应该注意的是,实际的资源在服务器上通常只有一种表示。 是客户端指定了它将以何种形式接受资源; 也就是说,它们应该如何被格式化。
SOAP、REST 和 XML-RPC——更仔细地看
简单对象访问协议(SOAP)是一个简单、轻量级、无状态、基于 xml 的协议,可用于分布式环境中异构系统之间的数据交换。 SOAP 可以用于传输数据,而不考虑所使用的平台和语言。 典型的 SOAP 消息格式如下:
<SOAP: Envelope>
<SOAP: Header>
</SOAP: Header>
<SOAP: Body>
</SOAP: Body>
</SOAP: Envelope>
下面的代码是一个 SOAP 请求的示例:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<ns1:RequestHeader
soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next"soapenv:mustUnderstand="0"xmlns:ns1="https://www.example.com/getData/P007">
<ns1:authentication xsi:type="ns1:ClientLogin" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ns1:token>SuchALongToken</ns1:token>
</ns1:authentication>
<ns1:networkCode>ABC-XYZ-0012345</ns1:networkCode>
<ns1:applicationName>Sample</ns1:applicationName>
</ns1:RequestHeader>
</soapenv:Header>
<soapenv:Body>
<getProductData >
<filterData>
<query>WHERE productId IS NOT NULL</query>
</filterData>
</getProductData>
</soapenv:Body>
</soapenv:Envelope>
下面的代码片段演示了前一个请求的 SOAP 响应:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<ResponseHeader >
<requestId>Some Request Id</requestId>
<responseTime>26</responseTime>
</ResponseHeader>
</soap:Header>
<soap:Body>
<getProductDataResponse >
<rval>
<totalResultSetSize>1</totalResultSetSize>
<startIndex>0</startIndex>
<results>
<productId>7</productId>
<productName>CTV</productName>
<description>Samsung LED Color Television</description>
<status>Active</status>
<productCode>P007</productCode>
</results>
</rval>
</getProductDataResponse>
</soap:Body>
</soap:Envelope>
请注意,SOAP 可以在没有 HTTP 协议的情况下使用,并且 SOAP 总是使用POST
操作。 SOAP 利用 XML 和无状态 HTTP 协议(如果与 HTTP 一起使用)来访问服务。 一个典型的 SOAP 请求看起来像以下代码:
GET /price HTTP/1.1
Host: http://localhost
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
xmlns:m="http://localhost/product">
<soap:Header>
<m:DeveloperKey>1</t>
</soap:Header>
<soap:Body>
<m:GetProductPrice>
<m:ProductCode>P001</m:ProductCode>
</m:GetProductPrice>
</soap:Body>
</soap:Envelope>
参考前面的代码片段,SOAP 请求的Body
部分包含发送的实际 XML 请求对象。 下面的代码片段演示了一个典型的 SOAP 响应:
HTTP/1.1 200 OK
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
xmlns:m="http://localhost/product">
<soap:Body>
<m:GetProductPriceResponse>
<m:Price>1008.78</m:Price>
</m:GetProductPriceResponse>
</soap:Body>
</soap:Envelope>
REST 是一种架构范例,用于建模如何在 Web 上表示、访问和修改数据。 REST 使用无状态 HTTP 协议和标准 HTTP 操作(GET
、PUT
、POST
和DELETE
)来执行 CRUD 操作。 REST 允许您完成 SOAP 和 XML-RPC 所能完成的所有工作。 除此之外,您还可以使用防火墙来提高安全性,并使用缓存来增强性能。 相同请求的 REST 版本很简单,如下所示:
GET /product?ProductCode=P001 HTTP/1.1
Host: http://localhost
对前一个请求的 REST 响应也同样简单。 如下代码片段所示:
HTTP/1.1 200 OK
<?xml version="1.0"?><m:Price xmlns:m="http://localhost/product">1008.78</m:Price>
XML-RPC 是一个基于 xml 的远程过程调用协议。 下面的代码片段是一个典型的 XML-RPC POST 请求的例子:
POST /product HTTP/1.1
Host: http://localhost
<?xml version="1.0"?>
<methodCall>
<methodName>product.GetProductPrice</methodName>
<params>
<param>
<value><string>P001</string></value>
</param>
</params>
</methodCall>
为了响应前面的 XML-RPC 请求,下面的代码片段是典型的 XML-RPC 响应的样子:
HTTP/1.1 200 OK
<?xml version="1.0"?>
<methodCall>
<methodName>product.GetProductPrice</methodName>
<params>
<param>
<value><double>1008.78</double></value>
</param>
</params>
</methodCall>
了解 Windows 通信基础
Windows Communication Foundation(WCF)是微软的一个框架,它在单一的保护伞下提供了分布式技术(Web Services、Remoting、COM+等)的统一。 WCF 框架于 2006 年作为。net Framework 3.0 的一部分首次引入。 它是一个由许多技术组成的框架,为设计基于 SOA 并具有相互通信能力的应用提供了一个平台。 根据微软,在http://msdn.microsoft.com/en-us/library/bb907578.aspx,
Windows Communication Foundation (WCF)是一个统一的框架,用于创建安全、可靠、事务处理和可互操作的分布式应用。 在 Visual Studio 的早期版本中,有几种技术可以用于应用之间的通信。
与 WCF 架构相关的三个最重要的概念包括服务、客户机和消息。 下图分析了 WCF 架构的构建块。
WCF 和。net 框架
与 WCF 架构相关的三个最重要的概念是:服务、客户机和消息。 WCF 中的契约可以有三种类型:服务契约、数据契约和消息契约。
WCF 采用基于契约的方法。 一个 WCFService
类至少实现一个服务契约。 服务契约是一个接口,用于定义由 WCFService
类公开的操作。 WCFService
类与任何其他. net 类一样,只是它被标记为ServiceContract
属性。 消息契约可以定义为一种允许您更改消息格式的方法。 注意,ServiceContract
、DataContract
和其他相关属性是在System.ServiceModel
名称空间中定义的。 WCF 中的绑定用于指定特定的服务如何与同类的其他服务和/或与其他客户端(也称为消费者)通信。
同样,任何在OperationContract
属性之前的方法在外部对 soap 可调用操作的客户机可见。 如果您有一个没有此属性集的方法,则该方法将不包含在服务契约中,因此 WCF 客户端将无法访问 WCF 服务的该操作。
下面是 WCF 中预定义的内置绑定的列表:
- BasicHttpBinding
- MsmqIntergrationBinding
- WSHttpBinding
- WSDualHttpBinding
- WSFederationHttpBinding
- NetTcpBinding
- NetNamedPipeBinding
- NetMsmqBinding
- NetPeerTcpBinding
WCF 中的端点用于将服务契约与其地址关联起来。 通道实际上是服务与其客户机之间的桥梁。 WCF 中支持的通道类型如下:
- 单纯形输入
- 单纯形输出
- 请求-应答
- 双工
注意,WCF 服务基于三个概念:地址、绑定和契约。 另外,WCF 服务和 WCF 客户端使用消息进行通信。 下图展示了如何在 WCF 中使用消息进行通信:
WCF 中的通信
这些消息可以依次具有以下模式之一:
- 单纯形
- 请求-应答
- 双工
WCF 4.5 提供了对基于 rest 的特性的改进支持。 在本节中,我们将首先实现一个简单的 WCF 服务,然后对其进行必要的修改,使其成为 RESTful 服务。 WCF 的新版本对基于 rest 的特性提供了改进的支持。
REST 属性
现在,让我们仔细看看 WCF REST 属性及其用途。 顺便说一下,所有这些属性都可以在System.ServiceModel.Web.dll
库中找到。 在本节中,我们将讨论在使用 RESTful 服务时经常使用的属性。
WebServiceHost
WebServiceHost
属性的使用简化了基于 web 的服务的托管。 它派生于ServiceHost
类并覆盖OnOpening
方法,并自动将WebHttpBehavior
类添加到端点。 下面的代码片段演示了如何使用WebServiceHost
属性:
WebServiceHost host = new WebServiceHost(typeof(ClassName), baseAddress);
WebHttpBinding binding = new WebHttpBinding();
host.AddServiceEndpoint(typeof(ISomeContract), binding, "WebServiceHost");
host.Open();
WebHttpBinding
WebHttpBinding
属性产生一个适当的基于 http 的传输通道。 在这里,安全性由WebHttpSecurity
类处理。 通过使用WebGet
属性或WebInvoke
属性,可以使用WebHttpBinding
绑定公开服务。
下面的代码片段演示了如何使用webHttpBinding
属性:
<configuration>
<system.serviceModel>
<services>
<service name="PacktService">
<endpoint binding="webHttpBinding" contract="PacktService"
behaviorConfiguration="webHttp"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webHttp">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<configuration>
WebHttpBehavior
WebHttpBehavior
属性定制基于 http 的调度逻辑,它覆盖操作选择、序列化和调用。 System.ServiceModel.Description
命名空间中的WebHttpBehavior
类如下所示:
public class WebHttpBehavior : IEndpointBehavior
{
// Properties
public virtual bool AutomaticFormatSelectionEnabled { get; set; }
public virtual WebMessageBodyStyle DefaultBodyStyle { get; set; }
public virtual WebMessageFormat DefaultOutgoingRequestFormat { get; set; }
public virtual WebMessageFormat DefaultOutgoingResponseFormat { get; set; }
public virtual bool FaultExceptionEnabled { get; set; }
public virtual bool HelpEnabled { get; set; }
protected internal string JavascriptCallbackParameterName { get; set; }
// Methods
public virtual void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);
protected virtual void AddClientErrorInspector(ServiceEndpoint endpoint, ClientRuntime clientRuntime);
protected virtual void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher);
public virtual void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime);
public virtual void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher);
protected virtual WebHttpDispatchOperationSelector GetOperationSelector(ServiceEndpoint endpoint);
protected virtual QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription);
protected virtual IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint);
protected virtual IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint);
protected virtual IClientMessageFormatter GetRequestClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint);
protected virtual IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint);
public virtual void Validate(ServiceEndpoint endpoint);
protected virtual void ValidateBinding(ServiceEndpoint endpoint);
}
WebOperationContext
WebOperationContext
属性用于访问方法中的 HTTP 细节。 您可以使用WebOperationContext.Current
属性检索当前上下文。 它为传入/传出的请求/响应上下文提供属性。
下面的代码片段演示了如何获取 HTTP 状态代码:
HttpStatusCode status = WebOperationContext.Current.IncomingResponse.StatusCode;
WebMessageFormat
此属性被用于控制服务中的消息格式。
注意事项
注意,WCF 支持两种主要的 web 格式:XML 和 JSON。
您可以使用RequestFormat
和ResponseFormat
属性控制消息的格式,如下代码所示:
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Employee GetData()
{
return new Employee
{
Firstname = "Joydip",
Lastname = "Kanjilal",
Email = "joydipkanjilal@yahoo.com";
};
}
WebGet
WebGet
属性使用GET
动词暴露操作。 换句话说,WebGet
属性用于通过使用 URI 映射将传入的 HTTPGET
请求映射到特定的 WCF 操作。 下面的代码片段显示了在System.ServiceModel.Web
命名空间中如何定义该属性:
[AttributeUsageAttribute(AttributeTargets.Method)]
public sealed class WebGetAttribute : Attribute, IOperationBehavior
下面是一个示例,说明如何使用WebGet
属性:
[OperationContract]
[WebGet(UriTemplate="/employee/{id}")]
public Employee GetEmployee(int id)
{
Employee empObj = null;
// Get employee object from the database
return empObj;
}
WebInvoke
WebInvoke
属性暴露了使用其他 HTTP 动词的服务,例如,如POST
、PUT
和DELETE
。 换句话说,WebInvoke
属性用于GET
请求之外的所有其他 HTTP 动词。 下面的代码片段显示了这个属性在System.ServiceModel.Web
命名空间中是如何定义的:
[AttributeUsageAttribute(AttributeTargets.Method)]
public sealed class WebInvokeAttribute : Attribute, IOperationBehavior
Here is an example that illustrates the usage of the WebInvoke attribute:
[OperationContract]
[WebInvoke(Method = "DELETE", UriTemplate = "/employee/{id}")]
public void DeleteEmployee(int id)
{
// Code to delete an employee record in the database
}
尿酸板
UriTemplate
类属于System.UriTemplate
,实现了允许您在 URI 空间中指定变量的 URI 模板语法。 UriTemplate
是一个表示 URI 模板的类。 UriTemplate
是一个 URI 字符串,包含用大括号括起来的变量({
,}
)。 请注意,UriTemplate
属性是在WebGet
和WebInvoke
属性上指定的,我们在前面使用这些属性来标识一个员工资源。
下面的代码片段演示了如何使用UriTemplate
:
[WebGet(UriTemplate = "RetrieveUserDetails/{userCode}/{projectCode}")]
public string RetrieveUserDetails(string userCode, string projectCode)
{
}
下表列出了重要的 HTTP 方法及其用法:
|
方法
|
描述
|
| --- | --- |
| GET
| 这用于请求特定资源的表示 |
| PUT
| 这用于创建或更新具有特定表示形式的资源 |
| DELETE
| 用于删除指定的资源 |
| POST
| 这用于提交将由特定资源处理的数据 |
| HEAD
| 这类似于 GET,但它只检索头信息 |
HTTP 协议还定义了一组标准状态码,用于指定特定请求的处理结果。 HTTP 的标准状态码及其用途如下表所示:
|
状态码
|
描述
| | --- | --- | | 100 | 信息 | | 200 | 成功的 | | 201 | 创建 | | 202 | 接受 | | 300 | 重定向 | | 304 | 不修改 | | 400 | 客户端错误 | | 402 | 付款要求 | | 404 | 没有找到 | | 405 | 方法不允许 | | 500 | 服务器错误 | | 501 | 没有实现 |
基于 rest 的 web 服务
RESTful web 服务(或 RESTful web API)是由一组资源组成的服务。 这些资源包括用于访问 web 服务的基本 URI、MIME 类型(即,JSON、XML 等)和一组定义的操作(即,POST
、GET
、PUT
或DELETE
)。 RESTful 服务与平台和语言无关。 然而,与 Web 服务不同的是,没有针对 RESTful 服务的任何官方标准设置。 REST 只是一种架构风格; 它没有任何标准。 使用 REST 的基本优点是传输中立性和使用高级WS-*
协议的便利。 REST 是可互操作的,使用简单,并且具有统一的接口。
学习 RESTful web 服务
REST 式 web 服务是基于 REST 架构范例的服务。 本质上,这些(也称为 RESTful Web API)是由一组资源组成的 Web 服务。 这些资源提供如下:
- 用于访问 web 服务的基本 URI
- MIME 类型,它定义了 web 服务支持的数据格式,即 JSON、XML 等等
- web 服务使用 HTTP 方法支持的一组操作,这些方法包括
POST
、GET
、PUT
或DELETE
与 web 服务类似,REST 服务是平台和语言独立的,基于 HTTP,甚至可以与防火墙一起使用。 请注意,与基于 SOAP 协议的 web 服务不同,RESTful 服务没有官方标准。 REST 只是一种架构风格,没有任何固定的标准。 下面的代码片段演示了一个 SOAP 请求的例子:
<?xml version = "1.0"?>
<soap:Envelope>
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
<soap:body emp="http://localhost/payroll">
<emp:GetEmployeeDetails>
<emp:EmployeeID>1</emp:EmployeeID>
</emp:GetEmployeeDetails>
</soap:Body>
</soap:Envelope>
下面的 url 显示了如何使用 REST 表示相同的内容:
http://localhost/payroll/EmployeeDetails/1
基于 rest 的 web 服务将 HTTP 方法映射到相应的 CRUD 操作。 前面的两个表显示了它们是如何映射的:
- HTTP 方法:CRUD 动作
- GET:检索资源
- POST:创建一个新的资源
- PUT:更新现有资源
- DELETE:删除已存在的资源
- HEAD:检索资源上的元数据信息
在。net 4.5 中实现 RESTful 服务
在本节中,我们将使用 WCF 实现 RESTful 服务。 WCF 是一个基于面向服务架构(SOA)的框架,用于设计分布式应用,这些分布式应用是具有相互通信能力的应用。 我们将在第三章,Working with rest 式服务中探讨更多关于 WCF 的内容。
UserNamePasswordValidator 类
在 WCF 的新版本中引入了UserNamePasswordValidator
类。 您可以使用这个类来设计和实现您自己的自定义验证器,以验证用户的凭证。
在 WCF 4.5 中,System.IdentityModel.Selectors
名称空间中的UserNamePasswordValidator
类可用于验证用户凭证。 您可以创建自己的自定义验证器,只需扩展UserNamePasswordValidator
类,然后重写Validate
方法,如下面的代码片段所示:
using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.ServiceModel;
namespace Packt
{
public class PacktValidator : UserNamePasswordValidator
{
public override void Validate(String userName, String password)
{
if (!userName.Equals("joydip")) || !password.Equals("joydip1@3"))
{
throw new SecurityTokenException("User Name and/or Password incorrect...!");
}
}
}
}
然后,您可以配置您刚刚在配置文件中创建的验证器,如以下代码所示:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<bindings>
<wsHttpBinding>
<binding name="PacktAuthentication">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="PacktValidator.ServiceBehavior">
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"customUserNamePasswordValidatorType="Packt.PacktValidator, Packt"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</services>
</system.serviceModel>
</configuration>
WCF 4.5 中的新增强包括以下内容:
- 简化的配置
- 标准的端点
- 发现
- 简化的 IIS 托管
- 其他改进
- 工作流服务
- 路由服务
- 自动帮助页面
简化配置
WCF 4.5 从默认的配置模型开始。 与之前的版本相比,WCF 4.5 中的配置要简单得多。 WCF 3。 X,您需要为服务主机指定端点、行为等。 在 WCF 4.5 中,提供了默认端点、绑定信息和行为。 从本质上说,当您实现特定的 WCF 服务时,WCF 4.0 消除了任何 WCF 配置的需要。
在 WCF 4.5 中,为任何 WCF 服务创建了一些标准端点和默认绑定/行为配置。 这使得开始使用 WCF 变得很容易,因为 WCF 3 繁琐的配置细节。 X 不再被要求。
考虑以下的 WCF 服务:
using System;
using System.ServiceModel;
namespace PacktService
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
String DisplayMessage();
}
public class TestService : ITestService
{
public String DisplayMessage()
{
return "Hello World!";
}
}
}
在 WCF 4.5 中,您可以使用ServiceHost
来托管 WCF 服务,而不需要任何配置信息。 下面的代码是所有你需要宿主你的 WCF 服务和显示地址,绑定和契约信息:
using System.ServiceModel;
using System;
using System.ServiceModel.Description;
namespace PacktClient
{
class Program
{
static void Main(string[] args)
{
ServiceHost serviceHost = new ServiceHost(typeof(PacktService.TestService));
serviceHost.AddServiceEndpoint(typeof(PacktService.TestService), new BasicHttpBinding(),"http://localhost:1607/TestService.svc");
serviceHost.Open();
foreach (ServiceEndpoint serviceEndpoint in serviceHost.Description.Endpoints)
Console.WriteLine("Address: {0}, Binding: {1}, Contract: {2}", serviceEndpoint.Address, serviceEndpoint.Binding.Name, serviceEndpoint.Contract.Name);
Console.ReadLine();
serviceHost.Close();
}
}
}
以下代码是 WCF 4.5 中需要指定的所有配置信息的示例:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
注意,默认情况下使用的是BasicHttpBinding
绑定。 如果您想选择更安全的绑定,例如WSHttpBinding
,您可以使用以下代码片段更改绑定信息:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme ="http"/>
</protocolMapping>
</system.serviceModel>
</configuration>
标准端点
标准端点是 WCF Framework 4.5 中预定义的端点。 您总是可以重用它们,但它们通常不会更改。
您可以通过使用端点名称在<configuration>
元素中引用以前的任何端点。 下面给出了一个相同的例子:
<configuration>
<system.serviceModel>
<services>
<service name="PacktService">
<endpoint kind="basicHttpBinding" contract="IMyService"/>
<endpoint kind="mexEndpoint" address="mex" />
</service>
</services>
</system.serviceModel>
</configuration>
发现
有两种操作模式。 它们如下:
- Ad-Hoc 模式:在此模式中,不存在集中式服务器,所有服务通知和客户端请求都以多播方式发送。
- 托管模式:拥有一台集中式服务器。 这样的服务器称为发现代理,其中集中发布服务,需要使用这种发布服务的客户端连接到该服务器以检索必要的信息。
您可以添加标准的udpDiscoveryEndpoint
端点,并启用<serviceDiscovery>
行为,以启用 Ad-hoc 模式下的服务发现。 下面的代码就是一个例子:
<configuration>
<system.serviceModel>
<services>
<service name="TestService">
<endpoint binding="wsHttpBinding" contract="ITestService" />
<!-- add a standard UDP discovery endpoint-->
<endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="TestService.MyServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to falsebefore deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceDiscovery />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
注意,在前面的代码片段中,添加了一个新端点来发现服务。 此外,还添加了ServiceDiscovery
行为。 您可以使用DiscoveryClient
类来发现您的服务并调用其中一个方法。
您必须创建DiscoveryClient
类的实例,并将UdpDiscoveryEndPoint
作为参数传递给该类的构造函数,以发现服务。 一旦发现了端点,就可以使用发现的端点地址来调用服务。 下面的代码片段说明了这一点:
using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
namespace PacktConsoleApplication
{
class Program
{
static void Main(string[] args)
{
DiscoveryClient discoverclient = new DiscoveryClient(new UdpDiscoveryEndpoint());
FindResponse findResponse = discoverclient.Find(new FindCriteria(typeof(ITestService)));
EndpointAddress endpointAddress = findResponse.Endpoints[0].Address;
MyServiceClient serviceClient = new MyServiceClient(new WSHttpBinding(), endpointAddress);
Console.WriteLine(serviceClient.DisplayMessage());
}
}
}
WCF 4.5 还支持配置服务,以便在服务启动时立即宣布它们的端点。 下面的代码展示了如何配置你的服务在它开始时宣布端点:
<configuration>
<system.serviceModel>
<services>
<service name="TestService">
<endpoint binding="wsHttpBinding" contract="ITestService"/>
<endpoint kind="udpDiscoveryEndpoint"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="udpAnnouncementEndpoint"/>
</announcementEndpoints>
</serviceDiscovery>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
简化 IIS 托管
在 IIS 上托管 WCF 4.5 应用现在变得容易多了。 下面给出一个简单的 WCF 服务示例:
<!-- PacktService.svc -->
<%@ ServiceHost Language="C#" Debug="true" Service=" PacktService
CodeBehind="~/App_Code/PacktService.cs" %>
[ServiceContract]
public class PacktService
{
[OperationContract]
public string GetMessage()
{
return "This is a test service.";
}
}
然后,您可以为应用的web.config
配置文件中的服务启用服务元数据,如下面的代码片段所示:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
您还可以在应用的web.config
配置文件中为您的 WCF 4.5 服务定义虚拟服务激活端点。 通过这样做,您可以激活您的 WCF 服务而不需要.svc
文件。 您需要在您的应用的web.config
配置文件中指定以下配置来激活您的服务,而不需要.svc
文件:
<configuration>
<system.serviceModel>
<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="PacktService.svc" service="PacktService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
</configuration>
REST 的改进
WCF 4.5 提供了对基于 rest 的特性的改进的支持。 现在,您已经支持了一个自动帮助页面,该页面描述了服务使用者或客户可用的基于 rest 的服务。 这个特性默认是打开的,但是你也可以手动配置它,如下面的代码清单所示:
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<behaviors>
<endpointBehaviors>
<behavior name="PacktTestHelpBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="PacktSampleWCFService">
<endpoint behaviorConfiguration="PacktTestHelpBehavior"binding="webHttpBinding"contract="PacktSampleWCFService" />
</service>
</services>
</system.serviceModel>
</configuration>
WCF 4.5 还支持 HTTP 缓存,这是通过使用AspNetCacheProfile
属性实现的。 注意,AspNetCacheProfile
支持实际上使用的是标准的 ASP.NET 输出缓存机制为您在 WCF 服务中提供缓存特性。
要使用此属性,您应该添加对System.ServiceModel.Web.Caching
命名空间的引用。 您可以在WebGet
操作中应用此属性,并指定您选择的缓存持续时间。 下面的代码片段可以在你的服务契约方法中使用这个特性:
using System.ServiceModel.Web.Caching;
[OperationContract]
[WebGet]
[AspNetCacheProfile("PacktCache")]
String GetProductName();
因此,你应该在你的应用的web.config
文件中设置缓存配置文件,如下所示:
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="PacktCache" duration="60" varyByParam="format"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
使用 WCF 4.5 实现 RESTful 服务
在本节中,我们将使用 WCF 4.5 实现我们的第一个 RESTful 服务。 要做到这一点,我们需要遵循以下步骤:
- 创建 WCF 服务
- 使服务 RESTful
- 指定绑定信息
- 托管 RESTful 服务
- 使用 RESTful 服务
创建 WCF 服务
一个典型的 WCF 实现应该有一个 WCF 服务和一个 WCF 客户端。 WCF 客户机将使用 WCF 服务提供的服务。
注意,WCF 服务包含:
- 一个
Service
班 - 一个托管环境
- 一个或多个端点
Service
类是使用一种针对。net CLR 的托管环境的语言编写的。 本质上,Service
类可以用任何你选择的。net 语言来编写(在本书中我们一直使用 c#)。 宿主环境是 WCF 服务将在其上下文中执行的环境。 端点使客户端或服务使用者能够访问 WCF 服务。
您可以从两个模板中选择创建 WCF 服务:Visual Studio 2013 WCF 服务库模板和 Visual Studio 服务应用模板。
让我们首先使用 Visual Studio WCF 服务库模板来创建一个 WCF 服务。 要做到这一点,请遵循以下步骤:
- 打开 Visual Studio 2013 IDE
- 导航到文件|新|项目
-
Select WCF Service Application from the list of templates displayed, as shown in the following screenshot:
创建 WCF 服务应用项目
-
为项目提供一个名称,然后单击OK保存。
创建一个 WCF 服务应用项目。 乍一看,Service
类看起来像以下代码:
using System;
namespace MyDataService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change
// the class name "Service1" in code, svc and config file together.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
前面的代码片段中的Service
类实现了IService1
接口,如下所示:
using System.Runtime.Serialization;
using System.ServiceModel;
namespace MyDataService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu
// to change the interface name "IService1" in both code
// and config file together.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below
// to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
考虑下面的 WCF 服务:
using System.ServiceModel;
namespace Test
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
String GetMessage();
}
public class TestService : ITestService
{
public string GetMessage()
{
return "Hello World!";
}
}
}
提示
注意在前面的代码片段中使用了OperationContract
属性。 此属性用于指定向客户端调用公开特定操作。
要托管这个 WCF 服务,你可以编写以下代码:
using System;
using System.ServiceModel;
using Test;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(TestService)))
{
serviceHost.Open();
Console.WriteLine("WCF Service has started...");
Console.ReadLine();
serviceHost.Close();
}
Console.WriteLine("The WCF Service has stopped...");
}
}
}
使服务 RESTful
在设计一个 RESTful 服务时,您需要了解资源、用于映射这些资源的 uri,以及这些资源应该支持的 HTTP 谓词。
在典型的基于 WCF-REST 的服务中,除了用于公开服务操作的OperationContract
属性外,还需要WebGet
属性。 WebGet
属性属于System.ServiceModel.Web
名称空间。 HTTP-GET 操作的典型WebGet
属性如下所示:
[WebGet(UriTemplate =
"/payroll/getemployees.xml",BodyStyle = WebMessageBodyStyle.Bare,RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml)]
属性WebGet
的UriTemplate
参数用于定义访问服务操作的 URL 格式。 RequestFormat
和ResponseFormat
参数是WebMessageFormat
枚举的一部分,可以有两个可能的值:Xml
和Json
之一。 下面的代码是 HTTP-POST 操作的典型WebGet
属性的样子:
[GetOperationContract]
[WebInvoke(UriTemplate =
"/payroll/updateemployee.xml?
employeecode={code}",Method = "POST",BodyStyle = WebMessageBodyStyle.Bare,RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml)]
要使我们在本章前面创建的服务是 RESTful 的,只需更改契约 ITest 并指定WebGet
属性,如下面的代码片段所示:
[ServiceContract]
[WebGet()]
public interface ITestService
{
[OperationContract]
String GetMessage();
}
指定绑定信息
现在我们已经创建了服务契约和服务,以及服务将要公开的操作,我们需要指定服务的绑定信息,以便服务消费者或客户可以对其进行评估。 为了让客户端访问 WCF 服务,服务应该公开端点。 端点表示服务的地址、绑定和契约信息。 要指定服务的绑定信息,请打开App.Config
文件,并在<system.serviceModel>
标记中写入以下代码:
<system.serviceModel>
<bindings>
</bindings>
<services>
<service name ="Test.TestService" behaviorConfiguration="Default">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Test"/>
</baseAddresses>
</host>
<endpointaddress=""binding ="basicHttpBinding"contract="Test.ITestService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
在我们的示例中,我们使用的是 RESTful 服务。 因此,我们需要使用webHttpBinding
类,如下所示:
<service name ="Test.TestService" behaviorConfiguration="Default">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Test"/>
</baseAddresses>
</host>
<endpointaddress=""binding ="webHttpBinding"contract="Test.TestService" />
</service>
托管 RESTful WCF 服务
托管 WCF 服务的方式有很多。 例如,您可以将 WCF 服务托管在 IIS 服务器中,或者使用Windows 激活服务(WAS)。 要在 IIS 中托管 WCF 服务,您必须创建一个虚拟目录,并使其指向您的服务所在的目录。 请注意,可以使用 HTTP 上的 SOAP 访问 IIS 中的 WCF 服务。
您可以在托管应用的App.Config
文件中指定访问 IIS 托管的 WCF 服务如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme ="http"/>
</protocolMapping>
</system.serviceModel>
</configuration>
托管控制台应用中的服务
如果端点定义正确,您还可以以编程方式托管 WCF 服务。 要托管我们之前创建的 RESTful WCF 服务,你应该使用WebServiceHost
,如下所示:
using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using Test;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
using (WebServiceHost serviceHost = new WebServiceHost(typeof(TestService)))
{
serviceHost.Open();
Console.WriteLine("WCF Service has started...");
Console.ReadLine();
serviceHost.Close();
}
Console.WriteLine("The WCF Service has stopped...");
}
}
}
返回 JSON 数据
您可以从基于 rest 的 WCF 服务中以JavaScript 对象标记(JSON)格式返回数据。 下面的代码片段演示了如何通过设置属性以 JSON 格式从 RESTful 服务返回数据:
using System.Xml;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
namespace Test
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebGet(UriTemplate = "Test")]
Message GetMessage();
}
public class TestService : ITestService
{
public Message GetMessage()
{
StringBuilder stringBuilder = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
{
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("Message");
xmlWriter.WriteAttributeString("type", "Name");
xmlWriter.WriteString("Joydip");
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
}
TextReader textReader = new StringReader(stringBuilder.ToString());
XmlReader xmlReader = XmlReader.Create(textReader);
return Message.CreateMessage(MessageVersion.None, "", xmlReader);
}
}
}
请注意,WCF 中的WebGet
属性实际上是 WCF 的 HTTP 编程模型的一部分。 此属性用于使用 HTTP URI 调用 WCF 服务。 本质上,WebGet
属性用于指定服务将响应 HTTPGET
请求。
属性WebGet
接受以下四个参数之一:
BodyStyle
:这是,用于指定是否应该封装请求或响应RequestFormat
:这是用于格式化请求消息ResponseFormat
:这是用于格式化响应消息UriTemplate
:用于指定服务操作的 HTTP 请求的 URI 模板
前面的代码片段使用了一个XmlWriter
实例来创建 XML 格式的消息。 下面是返回的 JSON 数据的样子:
{
"Message": [{ "Name":"Joydip"},]
}
使用 RESTful 服务
要使用我们在本节前面创建的 RESTful 服务,您需要创建一个客户机应用(ASP.NET 或 WPF),将服务引用添加到我们在前面几节中创建的服务,然后使用服务引用实例来调用服务方法。 我们将在第 3 章、与 rest 式服务打交道和第 4 章、使用 rest 式服务中详细讨论。
总结
REST 现在已经成为设计和实现可伸缩服务的架构范例。 基于 rest 的 Web 服务通过 uri 公开资源,并使用 HTTP 方法执行 CRUD 操作。 REST 架构范例不仅打开了许多可能性,也带来了挑战。 我们将在本书各章中进一步探讨这些问题。 本章介绍了 REST,一种分布式超媒体系统的架构风格。 在下一章中,我们将详细探讨面向资源的架构。
版权属于:月萌API www.moonapi.com,转载请注明出处