理解http幂等特性,安全操作

本文深入探讨HTTP协议中的幂等性概念,分析其在WebAPI设计中的重要性,对比分布式事务与幂等设计,详解HTTPGET、DELETE、PUT、POST方法的幂等性特征。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

理解HTTP幂等性

转自:http://www.cnblogs.com/weidagang2046/archive/2011/06/04/2063696.html

基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议,无论是服务器端提供Web服务,还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的Java语言并不意味着高质量的Java程序,简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。

幂等性定义

本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

从定义上看,HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要的概念,而HTTP的分布式本质也决定了它在HTTP中具有重要地位。

分布式事务 vs 幂等设计

为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为:

bool withdraw(account_id, amount)

withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:

non-idempotent

图1

这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

 
  1. int create_ticket()

  2. bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:

idempotent

图2

和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

HTTP的幂等性

HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。

HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。

比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:

The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。而PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有幂等性。

在介绍了几种操作的语义和幂等性之后,我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单,用POST /tickets来实现create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分,真正的URI应该是/accounts/account_id/ticket_id,而amount应该放在请求的body中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。

总结

上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。

参考

RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions
The Importance of Idempotence
Stackoverflow - PUT vs POST in REST

 

 

安全操作,幂等操作 

 

转自:http://blog.sina.com.cn/s/blog_4fd9844201010j0b.html

google了一些中文的资料, 基本了解了幂等是怎么回事儿. 备忘一下. 
PUT,DELETE操作是幂等的。所谓幂等是指不管进行多少次操作,结果都一样。比如我用PUT修改一篇文章,然后在做同样的操作,每次操作后的结果并没有不同,DELETE也是一样。顺便说一句,因为GET操作是安全的,所以它自然也是幂等的。 


POST操作既不是安全的,也不是幂等的,比如常见的POST重复加载问题:当我们多次发出同样的POST请求后,其结果是创建出了若干的资源。 


安全和幂等的意义在于:当操作没有达到预期的目标时,我们可以不停的重试,而不会对资源产生副作用。从这个意义上说,POST操作往往是有害的,但很多时候我们还是不得不使用它。 


还有一点需要注意的就是,创建操作可以使用POST,也可以使用PUT,区别在于POST是作用在一个集合资源之上的(/articles),而PUT操作是作用在一个具体资源之上的(/articles/123),再通俗点说,如果URL可以在客户端确定,那么就使用PUT,如果是在服务端确定,那么就使用POST,比如说很多资源使用数据库自增主键作为标识信息,而创建的资源的标识信息到底是什么只能由服务端提供,这个时候就必须使用POST。 


一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等操作对于代理和缓存来说具有“友好性”,因为幂等操作的额外执行不会对二者产生危害性后果(除了带宽浪费)。 
PUT方法是幂等的,幂等性意味着对于一个成功执行的请求,不管其执行多少次,其结果都是一致的。也就是说,只要你愿意,你可以用PUT方法对 Hotel资源进行任意次更新,其结果都一样。如果两个PUT方法同时发生,那么只有其中之一会赢得最后的胜利并决定资源的最终状态。删除操作也是幂等的,如果一个PUT方法和DELETE方法同时发生,那么资源或者被更新,或者被删除,而不可能停留在某个中间状态。 


如果你不确定是PUT还是DELETE被成功执行,并且没有得到状态码409 (Conflict)或者 417 (Expectation Failed)的话,那么就重新执行一遍。而不需要附加的可靠性协议来避免重复请求,因为通常重复的请求不会有任何影响。 


上述描述对于POST方法就不适用了,因为POST方法不是幂等的,若要两次执行同一个POST请求那就要注意了。POST方法所缺失的幂等性就解释了为什么当你每次重新发送POST请求时浏览器总是弹出警告。POST方法用于创建资源,而不需要由客户端指定实例id 


幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()”函数就是一个幂等函数,“deleteFile()”函数就不是。“幂等”是HTTP Session和EJB失败转移中的一个重要概念。 
当正在进行方法调用的时候失败了怎么办呢?答案是:处理过程中止,客户端看见错误消息提示(除非方法是幂等方法)。只有方法是幂等方法的情况,一些负载均衡器才能试图失败转移这些方法到别的实例。 


幂等为何如此重要?因为客户端并不知道服务器何时失败的(在方法刚开始调用或者快要调用完成的时候)。如果是非幂等方法,则两次调用就会两次改变系统状态,系统就会处于不一致的状态。 


在复杂应用中,不太可能把所有的方法都变成幂等方法。所以,只能通过失败转移减少错误,而不可能从根本上避免错误。 


把应用程序设计为幂等的:幂等的的意思就是一个操做不会修改状态信息,并且每次操作的时候都返回同样的结果(换句话说就是:做多次和做一次的效果是一样的),通常,WEB请求,特别是 HTML forms 都被发送多次(当用户点击发送按纽两次,重载页面多次),导致多次HTTP请求。设计SERVLET和其他WEB对象为 幂等的,可以容忍多次请求。详细可以去参考设计模式“Synchronized Token ”和“Idempotent Receiver ”关于怎样设计幂等的的应用程序。

<think>好的,我现在需要回答用户关于HTTP请求方法中GET和POST的区别,特别是安全性与等性的问题。首先,我需要回顾一下HTTP协议的基础知识,确保自己理解正确。 用户之前询问了TCP的三次握手和四次挥手,现在转向应用层的HTTP方法,说明他们可能在准备面试或学习网络协议。因此,回答需要结构清晰,重点突出区别,并且涵盖常见的面试考点,比如安全性和等性。 首先,我应该明确HTTP的常见方法,但用户主要关注GET和POST。需要先分别解释这两个方法的基本用途,然后对比它们的区别。安全性和等性是关键点,这两个概念需要准确解释,避免混淆。 安全性在HTTP中的定义是指方法是否会对服务器资源产生修改。根据RFC规范,安全的方法不应该修改资源,所以GET是安全的,而POST不是。但要注意,虽然GET被设计为安全,但实际应用中如果服务器端处理不当,可能通过GET请求修改数据,但这不符合规范。需要强调规范定义与实际实现的区别。 等性是指多次执行同一请求的效果是否相同。GET、PUT、DELETE都是等的,而POST不是。等性对客户端重试和网络通信很重要,比如在请求失败时,等方法可以安全地重试,而非等方法则可能需要额外处理。 接下来需要考虑其他区别,比如参数位置:GET参数在URL中,POST在请求体中。这导致GET的参数有长度限制,且可能被日志记录,存在安全隐患,比如密码不应该用GET传输。此外,缓存机制不同,GET可以被缓存,POST通常不行。 用户可能还关心实际应用中的选择,比如何时用GET何时用POST。需要给出例子,比如获取数据用GET,提交表单用POST。同时,可以提到RESTful API设计中对这些方法的应用。 在测试方面,可能需要验证GET请求是否没有副作用,POST是否正确处理数据,等性是否正确实现。比如,重复提交POST可能导致多次创建资源,而重复GET则不会改变资源状态。 需要注意常见的误解,比如认为POST比GET更安全。虽然POST数据不在URL中,但如果不使用HTTPS,数据依然可能被截获。安全性更多依赖于传输加密而非方法本身。 最后,总结关键点,确保覆盖安全性和等性,以及其他主要区别。结构清晰,分点列出,便于用户理解和记忆。可能还需要准备一些面试题示例,比如“为什么POST不是等的?”或“GET请求可以有body吗?”(虽然规范不禁止,但一般不建议) 需要检查是否有遗漏点,比如其他HTTP方法(如PUT、DELETE)的提及是否必要,但用户问题明确是GET vs POST,所以可能不需要展开。确认所有技术细节准确,比如等性的定义是否符合RFC标准,避免错误。 现在组织这些思路,分步骤解释,确保逻辑连贯,示例恰当,并且符合用户要求的格式,使用中文,行内公式正确(虽然这个问题可能不需要数学公式,但如果有相关内容需要表达,比如编码方式,可能需要使用)。不过当前问题可能不涉及数学表达式,所以主要使用清晰的结构和重点突出的对比。</think>以下是关于**HTTP请求方法GET与POST区别**的详细解析,重点关注安全性与等性,并结合实际场景与面试问题说明: --- ### **一、核心区别概览** | **特性** | **GET** | **POST** | |----------------|----------------------------------|----------------------------------| | **设计目的** | 获取资源(查询) | 提交数据(修改资源) | | **参数位置** | URL末尾(`?key=value`) | 请求体(Body) | | **数据长度限制**| URL长度限制(通常≤2048字符) | 无硬性限制 | | **缓存** | 可被缓存 | 通常不被缓存 | | **历史记录** | 参数保留在浏览器历史 | 参数不保留 | | **安全性** | 不安全(参数暴露) | 相对安全(但依赖HTTPS) | | **等性** | 等 | 非等 | --- ### **二、关键概念深入** #### **1. 安全性(Safety)** - **定义**:HTTP方法是否会对服务器资源产生修改(RFC 7231标准)。 - **GET的安全性**: - 设计上**不应对服务器数据产生副作用**(仅用于读取)。 - *实际风险*:若服务端错误实现GET接口(如用GET删除数据),仍可能导致数据修改。 - *测试用例*:验证GET请求是否触发非查询操作(如数据库写操作)。 - **POST的非安全性**: - 设计用于**提交数据**(如创建订单、修改用户信息)。 - *典型场景*:表单提交、文件上传。 #### **2. 等性(Idempotency)** - **定义**:**多次执行同一请求**的效果与**单次执行相同**。 - **GET的等性**: - 多次获取同一资源不会改变服务器状态(如刷新页面)。 - *测试验证*:重复发送GET请求,检查响应数据一致性。 - **POST的非等性**: - 多次提交可能产生**不同结果**(如重复提交订单会创建多个订单)。 - *解决方案*:服务端需实现防重机制(如Token校验、唯一ID)。 --- ### **三、实际场景与面试问题** #### **1. 参数传输方式** - **GET示例**: ```bash GET /search?q=HTTP+methods HTTP/1.1 Host: example.com ``` - *风险*:敏感数据(如`token`)通过URL暴露,可能被浏览器历史或日志记录。 - **POST示例**: ```bash POST /submit-form HTTP/1.1 Host: example.com Content-Type: application/json {"username": "test", "password": "123456"} ``` - *注意*:即使使用POST,未加密传输(HTTP)仍可能被中间人截获。 #### **2. 缓存机制** - **GET缓存优化**: - 浏览器自动缓存GET响应,可通过`Cache-Control`头控制。 - *测试关注点*:验证缓存策略是否导致数据过期(如商品价格未更新)。 - **POST禁用缓存**: - 默认不缓存,需显式设置`Cache-Control: no-store`。 #### **3. 高频面试题** - **“POST比GET更安全吗?”** - **答案**: - **传输安全性**:两者在不使用HTTPS时均不安全。 - **设计安全性**:POST参数不在URL中,减少了被直接暴露的风险。 - **“如何实现等的POST请求?”** - **方案**: 1. 服务端生成唯一ID(如UUID),拒绝重复ID的请求。 2. 客户端预先生成ID并附带在请求中。 - **“GET请求可以有Body吗?”** - **RFC规范**:允许,但**无实际意义**(服务器可能忽略)。 - *实际开发*:避免使用,主流框架(如Spring)可能不支持解析GET的Body。 --- ### **四、测试与调试技巧** 1. **抓包验证**: - 使用**Charles**或**Wireshark**检查GET/POST参数位置与加密情况。 2. **等性测试**: - 对POST接口重复发送相同请求,观察是否产生多个资源。 3. **安全扫描**: - 使用**OWASP ZAP**检测GET参数是否泄露敏感信息。 --- ### **五、总结** - **选择依据**: - 用**GET**:获取数据、过滤查询、书签分享。 - 用**POST**:提交数据、涉及敏感信息、非操作。 - **核心注意**: - 安全依赖HTTPS,而非仅靠POST! - 等性是服务端逻辑设计的关键约束。 ```plaintext # 示例:RESTful API设计原则 GET /users → 获取用户列表 POST /users → 创建新用户(非等) GET /users/{id} → 获取单个用户 PUT /users/{id} → 全量更新用户(等) DELETE /users/{id} → 删除用户(等) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值