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和DELETEHTTP 动词的“代码选项”。


