初学者教程:创建 WCF REST 服务






4.82/5 (102投票s)
在本文中,我们将尝试了解 WCF REST 服务是什么以及如何创建它。
引言
在本文中,我们将尝试了解 WCF REST 服务是什么。我们将从服务开发人员的角度了解创建启用 REST 的 WCF 服务需要什么。我们将了解如何使用和消费 restful WCF 服务。
免责声明:这是一篇相当老的文章(差不多 5 年了)。我想在这里做个免责声明 - 这不是一个 REST 服务。本文讨论了 REST,但最终使用 WCF 创建了基于 HTTP 的服务。这个创建的 WCF 服务不遵循 REST 架构指南。对此表示歉意(我想我们都会随着时间的推移变得更聪明)。我将更新这篇文章,但在此之前,我觉得这个免责声明应该存在。
背景
REST 概述
REST
代表 Representational State Transfer
(表述性状态传输)。这是一种在分布式环境中交换数据的协议。REST 背后的主要思想是,我们应该将分布式服务视为一种资源,并且我们应该能够使用简单的 HTTP 协议对该资源执行各种操作。
当我们谈论数据库作为资源时,我们通常会谈论 CRUD
操作。即 Create(创建)、Retrieve(检索)、Update(更新)和 Delete(删除)。现在 REST 的哲学是,对于远程资源,所有这些操作都应该是可能的,并且它们应该可以使用简单的 HTTP 协议实现。
现在基本的 CRUD 操作以以下方式映射到 HTTP 协议
- GET:这映射到 CRUD 操作的
R(检索)
部分。这将用于从远程资源检索所需数据(数据的表示)。 - POST:这映射到 CRUD 操作的
U(更新)
部分。此协议将更新远程服务器上数据的当前表示。 - PUT:这映射到 CRUD 操作的
C(创建)
部分。这将为当前发送到服务器的数据创建一个新条目。 - DELETE:这映射到 CRUD 操作的
D(删除)
部分。这将从远程服务器删除指定的数据。
因此,如果我们以一个包含图书列表数据库的远程资源为例。图书列表可以使用以下 URL 检索
www.testwebsite.com/books
要检索任何特定的书籍,假设我们有一些可用于检索书籍的 ID,可能的 URL 可能如下所示
www.testwebsite.com/books/1
由于这些是 GET
请求,数据只能从服务器检索。要执行其他操作,如果我们使用类似的 URI 结构和 PUT
、POST
或 DELETE
操作,我们应该能够从服务器创建、更新和删除资源。我们将在实现部分看到如何完成此操作。
注意: 使用这些 URL 结构可以执行更多复杂的查询。我们不会讨论可以使用各种 URL 模式执行的完整查询操作集。
使用代码
现在我们可以创建一个简单的 WCF 服务,它将在某个数据库上实现所有基本的 CRUD 操作。但是要使此 WCF 服务与 REST 兼容,我们需要在配置、服务行为和契约中进行一些更改。让我们看看我们将创建什么样的 WCF 服务,然后我们将看到如何使其在 REST 协议上可用。
创建启用 REST 的 ServiceContract
我们将创建 Books 表并尝试在此表上执行 CRUD 操作。
为了在服务中执行数据库操作,让我们使用 Entity Framework。这可以通过使用 ADO.NET 调用或其他 ORM 来很好地完成,但我选择了 Entity Framework。(请参阅此文章了解 Entity Framework: 绝对初学者 Entity Framework 简介[^])。生成的实体将如下所示。
现在服务契约将包含 CRUD 操作的函数。让我们为该服务创建 ServiceContract
[ServiceContract]
public interface IBookService
{
[OperationContract]
List<Book> GetBooksList();
[OperationContract]
Book GetBookById(string id);
[OperationContract]
void AddBook(string name);
[OperationContract]
void UpdateBook(string id, string name);
[OperationContract]
void DeleteBook(string id);
}
目前这是一个非常简单的服务契约,为了指示单个操作可以通过 REST 协议调用,我们需要用附加属性来修饰这些操作。那些将在 HTTP GET
协议上调用的操作,我们需要用 WebGet
属性来修饰它们。那些将由 POST、PUT、DELETE
等协议调用的操作将用 WebInvoke
属性来修饰。
理解 UriTemplate
现在在将这些属性添加到这些操作之前,让我们首先了解 UriTemplate
的概念。UriTemplate
是 WebGet 和 WebInvoke
属性的一个属性,它将帮助我们将来自 HTTP 协议的参数名称与 ServiceContract
的参数名称进行映射。例如,如果有人使用以下 URI
localhost/testservice/GetBookById/2
我们需要将此第一个参数映射到函数的 id 变量。这可以通过使用 UriTemplate
来完成。此外,我们可以专门为 URI 更改函数名称,并且 URI 函数名称将映射到实际的函数名称,即如果我们像这样调用相同的 URL
localhost/testservice/Book/2
那么我们可以通过将操作的 UriTemplate
指定为
[OperationContract]
[WebGet(UriTemplate = "Book/{id}")]
Book GetBookById(string id);
按照同样的思路,让我们也为其他方法定义 UriTemplate
。
[ServiceContract]
public interface IBookService
{
[OperationContract]
[WebGet]
List<Book> GetBooksList();
[OperationContract]
[WebGet(UriTemplate = "Book/{id}")]
Book GetBookById(string id);
[OperationContract]
[WebInvoke(UriTemplate = "AddBook/{name}")]
void AddBook(string name);
[OperationContract]
[WebInvoke(UriTemplate = "UpdateBook/{id}/{name}")]
void UpdateBook(string id, string name);
[OperationContract]
[WebInvoke(UriTemplate = "DeleteBook/{id}")]
void DeleteBook(string id);
}
实现服务
现在服务实现部分将使用 Entity Framework 生成的上下文和实体来执行所有相应的操作。
public class BookService : IBookService
{
public List<Book> GetBooksList()
{
using (SampleDbEntities entities = new SampleDbEntities())
{
return entities.Books.ToList();
}
}
public Book GetBookById(string id)
{
try
{
int bookId = Convert.ToInt32(id);
using (SampleDbEntities entities = new SampleDbEntities())
{
return entities.Books.SingleOrDefault(book => book.ID == bookId);
}
}
catch
{
throw new FaultException("Something went wrong");
}
}
public void AddBook(string name)
{
using (SampleDbEntities entities = new SampleDbEntities())
{
Book book = new Book { BookName = name };
entities.Books.AddObject(book);
entities.SaveChanges();
}
}
public void UpdateBook(string id, string name)
{
try
{
int bookId = Convert.ToInt32(id);
using (SampleDbEntities entities = new SampleDbEntities())
{
Book book = entities.Books.SingleOrDefault(b => b.ID == bookId);
book.BookName = name;
entities.SaveChanges();
}
}
catch
{
throw new FaultException("Something went wrong");
}
}
public void DeleteBook(string id)
{
try
{
int bookId = Convert.ToInt32(id);
using (SampleDbEntities entities = new SampleDbEntities())
{
Book book = entities.Books.SingleOrDefault(b => b.ID == bookId);
entities.Books.DeleteObject(book);
entities.SaveChanges();
}
}
catch
{
throw new FaultException("Something went wrong");
}
}
}
Restful WCF 服务配置
现在从 ServiceContract
的角度来看,服务已准备好服务 REST 请求,但要通过 REST 访问此服务,我们还需要对服务行为和绑定进行一些更改。
要使服务通过 REST 协议可用,需要使用的绑定是 webHttpBinding
。此外,我们需要设置端点的行为配置并在 endpointBehavior
中定义 webHttp
参数。因此,我们最终的配置将如下所示
测试服务
现在,为了测试服务,我们只需运行服务并使用 URL 来检索数据。让我们看看我们的 GET
操作的实际情况。
现在测试获取单个记录的查询
我们已经看到,我们在浏览器中以 XML 格式收到了响应。我们可以使用这些 URL 和 HTTP 协议,而无需通过添加服务引用来使用此服务。
注意: 这里我没有演示 POST、PUT 和 DELETE 的其他操作,但它们都相当直接,一个简单的 HTML 页面使用所需的协议和指定的参数名称发送数据即可执行操作。
使用 JSON
我们还可以将响应和请求格式更改为使用 JSON
而不是 XML
。为此,我们需要指定 WebInvoke
属性的属性。
RequestFormat
:默认值为WebMessageFormat.XML
。要将其更改为 JSON 格式,需要将其设置为WebMessageFormat.Json
。ResponseFormat
:默认值为WebMessageFormat.XML
。要将其更改为 JSON 格式,需要将其设置为WebMessageFormat.Json。
让我们在我们的服务契约中再创建一个名为 GetBooksNames
的操作,并将此方法的 ResponseFormat
设置为 Json。
[OperationContract]
[WebGet(ResponseFormat=WebMessageFormat.Json]
List<string> GetBooksNames();
响应现在将以 JSON 格式显示。
现在我们已经有了一个 WCF REST 服务。
注意: Visual Studio 中也有现成的模板用于创建 WCF 数据服务,它为我们提供了一种轻松创建启用 REST 的 ODATA 服务的方法。我们可能会单独讨论它们。
关注点
在本文中,我们已经了解了如何创建支持 REST 的 WCF 服务。我们还查看了一个实现相同功能的示例应用程序。很多内容都留作读者的练习(例如使用 POST、PUT 和 DELETE 的操作),但我仍然希望这能提供一些信息。
历史
- 2013 年 4 月 3 日: 第一版。