REST 自行承担风险!






4.53/5 (17投票s)
REST – Representational State Transfer,实现
引言
我知道,标题可能具有误导性,但对于 REST – Representational State Transfer,简单性伴随着风险 - 如果您不理解并且没有正确实现 REST。REST 并不像看起来那么简单。这个概念看起来足够简单,但细节才是关键,我们很快就会看到。这篇文章将尝试提出一些关于其实现以及可能的解决方案的问题。
首先,关于 REST 的内容太多了,令人眼花缭乱……即:
REST 不是规范,而是理论。整个概念的简洁性和强大之处。REST 是基于 HTTP 的 CRUD,但又不是!GET
、POST
、PUT
、DELETE
动词构成了 REST。没有 SOAP,没有 RPC – 开发简单,实现更简单。关于 WCF REST 服务和 Web API 的所有争论都不是 REST,它们是基于 HTTP 的 RPC GET
和 POST
在 HTTP 上工作,但 PUT
和 DELETE
不行。
其次,当您开始实现时,会遇到许多问题(服务器端和客户端),您可能会遇到。
这篇文章将尝试以最简单的方式解释 REST,并提供一个非常非常基础的实现,并尝试展示其在实际实现中的一些问题,并提出一些解决这些问题的想法。
我们将演示以下内容
我们将创建一个通用处理程序/IHTTPHandler
,它将代表我们的 REST 服务。HTTPHandler
将返回给我们一个书籍集合。我们将能够通过 HTTP 对该集合进行添加、更新、删除和查询。REST 服务将使用 Session
来存储其数据。
注意:REST 不鼓励在服务器端进行 Session
状态管理,我们只是为了本次演示而使用它,作为物理数据存储的替代方案。要测试我们的 REST 服务,我们将使用一个 HTTP 客户端 - Postman - Chrome 浏览器中的 REST 客户端。我们将展示 GET
和 POST
请求如何工作,以及 PUT
和 DELETE
如何以及为什么不起作用,并将展示如何修复这些问题。
我们将不展示以下内容
我们不会详细解释或讨论 REST 作为规范/实践。我们不会创建花哨的 UI 或客户端来使事情复杂化。我们不使用 WCF REST 服务或 ASP.NET Web API,因为我们想演示概念的简单性以及实现中的问题。
注意:WCF REST 服务或 ASP.NET Web API 都以 HTTPHandler
作为其一切的基础。
REST 服务不与任何后端或数据源连接,我们也不展示数据访问或 Entity Framework!
实现
我们开始创建一个新的 ASP.NET 空 Web 应用程序,并将其命名为 RESTService-Demo1
。
在这个新创建的项目中,我们添加一个新文件夹 - Data。我们在新文件夹中添加一个类 Book.cs 和一个通用处理程序 BooksHandler.ashx。
我们的 Books.cs 将如下所示
我们的通用处理程序 BooksHandler.ashx – 将如下所示

上面突出显示了一些要点,并将在下面详细介绍。
Handler [BooksHandler]
实现 IHttpHandler
,它有一个成员 ProcessRequest()
。由于我们还需要访问 Session
,因此我们实现了 IRequiresSessionState
接口。当调用 Handler
并执行 ProcessRequest()
方法时,我们可以通过 context 参数访问请求的类型、客户端发送的数据以及更多信息。根据 RequestType
,我们调用 REST 服务中的相应方法。
GetBooks()
方法如下所述
我们检查客户端是否在请求中传递了 BookId
,如果没有,则在一个循环中构造 Books
列表 [5] 并将其存储在 session 中。我们通过 ReturnBooksToClient()
方法将 Books
列表以 JSON 格式返回给客户端。
如果用户对特定 Book
感兴趣,他/她会传递其 BookId
,然后我们选择“bookToReturn
”这本书并将其返回给客户端。
我们的默认 GET
请求及其响应如下所示。这里我们没有传递任何 BookId
,因此返回了我们列表中的所有书籍。

当我们传递 BookId
时,我们在响应中获得该书籍。请注意,这里 BookId
是作为查询字符串传递的。
在我们的 POST
请求中,我们传递了一个 BookId
。我们的 UpdateBook()
方法会在我们的 Books
集合中查找 BookId
,并通过在其 Name、Authors 和 Description 数据成员后附加“- Updated”来更新它。然后,我们按如下方式返回集合中的所有书籍……
在实际应用程序中,我们需要从客户端传递要更新的数据。这只是一个演示,所以我们在此处放宽了一些规则。
现在让我们尝试通过传递 PUT
请求将一本新书插入到我们的集合中。我们的处理程序已经包含了处理请求和更新我们书籍集合的所有代码,其 AddBook()
成员。
结果发现,我们无法将 PUT
甚至 DELETE
请求传递给我们的处理程序。我们收到以下错误:“HTTP 错误 405.0 - 不允许的方法”。
从错误中可以清楚地看出,传递给我们的处理程序的 RequestType
(也称为 HTTP Verb 或 Method):PUT
和 DELETE
是不允许的。
现在,百万美元问题是:谁不允许这些动词,为什么?如果真是这样,那么我们的 REST 服务(也称为通用处理程序)将如何工作,以及它在其他地方是如何工作的?
百万美元的答案是:我们托管在 IIS 上的 Web 应用程序未配置为处理 PUT
和 DELETE
请求。通常,GET
和 POST
请求足以通过 HTTP 进行通信。事实上,ASP.NET 主要使用 POST
。回发有印象吗?
PUT
和 DELETE
在某种程度上被认为是危险的。请注意,我不是想危言耸听,但拒绝 PUT
和 DELETE
是有充分理由的。PUT
用于创建,DELETE
用于删除,显而易见——不允许应用程序用户访问这些动词意味着用户无法直接创建或删除任何内容。这就是为什么默认情况下不允许这两个动词。
但是 REST 希望使用这些动词!因此,我们将确保我们的 REST 服务得到它所需要的。
解决 PUT 和 DELETE 困境的方案
选项 1: 配置…
以下是允许我们在应用程序中进行PUT
和 DELETE
操作的配置。为了允许我们的处理程序(也称为 REST 服务)通过 HTTP 进行 CRUD [PUT
、GET
、POST
和 DELETE
] 操作,我们需要以下设置
应用程序需要托管在 IIS Web 服务器上。
需要应用程序池,具有
.NET 版本框架 4.0
管道模式:集成注意:我目前托管在 .NET 运行时 4.0 上,但配置将与旧 .NET 版本的运行时几乎相同。
web.config 中所需的设置是

重要设置已突出显示。更多详细信息可以在 这里找到。
在对 IIS 上的应用程序进行更改后,PUT
和 DELETE
开始工作。选项 2: 代码…
在某些情况下(如下所列),我们可能无法使用上述解决方案配置我们的应用程序。
- 我们的系统或 Web 服务器管理员不允许我们的应用程序拥有
PUT
或DELETE
权限。 - 我们需要在您的服务中维护 CRUD 实现(
GetBooks()
、AddBook()
、DeleteBook()
),但为您的客户端提供一种正式的替代方法来发出PUT
或DELETE
请求。
PUT
或 DELETE
请求封装在 POST
请求中,并带有“X-HTTP-Method-Override
”标头。我们既不需要 IIS 端也不需要 web.config 文件进行任何特殊配置。我们将使用前一个示例中的代码作为基础,并在其之上进行构建。代码将以 RESTService-Demo2
的形式提供。
我们开始,通过删除上面示例中添加到 web.config 文件的处理程序配置。注意:如果我们希望同时支持直接的 PUT
和 DELETE
请求以及动词隧道解决方案,则可以保留 web.config 中的原始配置。
如果更新,我们的 web.config 文件将非常简单,如下所示
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation targetframework="4.5" debug="true">
<httpruntime targetframework="4.5">
</httpruntime>
</compilation>
</system.web>
</configuration>
接下来,我们在项目的根目录中添加一个全局应用程序类 – Global.asax 文件。在该文件中,我们在其 Application_BeginRequest
事件中添加以下代码。
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Request.RequestType.ToUpper() == "POST")
{
if (Request.Headers.AllKeys.Contains("X-HTTP-Method-Override", StringComparer.OrdinalIgnoreCase))
{
string overrideMethod = Request.Headers.Get("X-HTTP-Method-Override");
if (overrideMethod.Contains("PUT") || overrideMethod.Contains("DELETE"))
Request.RequestType = overrideMethod;
}
}
}
Application_BeginRequest()
事件在我们的应用程序收到的每个请求上都会执行。在该事件中,我们检查请求的类型是否为“POST
”,并且请求标头是否包含“X-HTTP-Method-Override
”条目。如果包含,我们将请求类型从 POST
更改为“X-HTTP-Method-Override
”标头中传递的值。
我们将继续从前面的示例开始,并使用 Postman - REST Client 测试我们的代码。我们的 GET
和 POST
请求/响应将保持不变,因此我们将重点关注 PUT
和 DELETE
请求。
我们更新的 DELETE
请求如下所示

这里,我们在响应中缺少书籍 #1,证明我们的 DELETE
操作已成功。
我们更新的 PUT(Insert)
请求如下所示。

这里,我们在响应中新增了书籍 #6,证明 PUT
操作也已成功。
这些请求将到达我们应用程序文件(Global.asax)中的 Application_BeginRequest()
事件,并被转换为相应的 PUT
和 DELETE
请求。
在这里,我们的应用程序、IIS 配置或我们的系统管理员都不会遇到麻烦,因为所有请求都以 GET
或 POST
的形式进来,这两者都被认为是安全的。
我们的客户端,如果无法直接发出 PUT
或 DELETE
调用,则需要通过请求标头“X-HTTP-Method-Override
”来传递他们的请求。
对于那些能够发出 PUT
或 DELETE
请求的客户端,只要我们保留了前面选项中的 web.config 文件,就没有变化。
至此,我们演示了使用通用处理程序作为 REST 服务进行 HTTP 上的 CRUD 操作。以下是在采用所述技术之前需要考虑的几个非常重要的点。
- 演示仅用于演示目的,在实际应用程序中,我们的 REST 服务将有更多功能,如身份验证、授权和验证。
- 在大多数情况下,您的系统管理员不会允许您的应用程序拥有
DELETE
和PUT
权限。例如,在我们的例子中,我们可以随意地对我们的应用程序执行PUT
和DELETE
请求!在实际应用程序中,这是不可想象的。这并不是说我们不能或不应该这样做,但所有优点和缺点都需要考虑。 - 可能会有防火墙阻止
PUT
和DELETE
请求。 - 如果使用了动词隧道选项来执行
PUT
和DELETE
,则需要告知客户端在请求标头中发送“X-HTTP-Method-Override
”。
我希望现在您能明白为什么我说“自行承担风险使用 REST!”
下次我将尝试写一些有关我们微型 REST 服务客户端的内容。在此之前……
历史
- 2014 年 3 月 4 日:原始帖子
- 2014 年 5 月 1 日:添加了用于允许
PUT
和DELETE
HTTP 动词的“代码选项”。