Python 通过cookie注入状态

Python 通过cookie注入状态,Cookie的引入改变了客户端和服务器之间的整体关系,使之状态化了,然而没有涉及对HTTP协议本身的修改。状态信息通过请求和响应的报头进行通信。服务器会在响应报头中向用户代理发送cookie。用户代理会在请求报头中保存并使用cookie进行响应。

用户代理或浏览器需要保留cookie值的缓存,将它作为响应的一部分来提供,并在后续的请求中包含相应的cookie。Web服务器会在请求报头中查找cookie,并在响应报头中提供更新了的cookie。这样做的效果是Web服务器变为无状态的,而状态的改变只在客户端发生。服务器将cookie视为请求中的额外参数,并会在响应中提供一些附加的细节信息。

Cookie可以包含任何内容。通常会将它们加密以避免Web服务器的详细信息暴露给客户端计算机上运行的其他应用程序。传输巨大的cookie会拖慢处理速度。优化这种状态处理的最佳方法是使用现有框架。我们将忽略cookie和会话管理的细节。

会话的概念是Web服务器的一个特性,而不是HTTP协议。通常将会话定义为具有相同cookie的一系列请求。在发出初始请求时,由于没有可用的cookie,因此会创建一个新的会话cookie。每个后续请求都将包含该cookie。一个登陆用户的会话cookie中会包含其他细节信息。只要服务器还继续接收该cookie,会话便可以一直存续,即cookie可以永久有效,或者在几分钟后失效。

Web服务的REST方法不依赖会话或cookie。由于每个REST请求都不同,因此相比使用cookie来简化用户交互的交互式网站,它不是那么用户友好
本教程关注基于REST的Web服务是因为它们非常符合函数式设计模式。

使用无会话REST进程的一个结果是每个单独的REST请求都会被单独验证。如果需要身份认证,则意味着REST通信必须使用SSL(secured socket layer,安全套接字层)协议。可以使用https方案将凭证从客户端安全地传输到服务器。

函数式设计的服务器考量

HTTP背后的一个核心思想是服务器响应是对请求的函数。从概念上讲,Web服务应该具有顶层实现,概括如下:

response = httpd(request)

然而这样做是不切实际的。事实证明,HTTP请求并不是简单的、单一的数据结构,它包含一些必要的内容和可选的内容。请求可能含有报头、方法和路径,也可能有附件。这里的附件可能包括表单或上传的文件,或两者兼有。
如果情况再复杂一些,那么可以将浏览器的表单数据作为GET请求路径中的查询字符串来发送,或者将其作为附件发送给POST请求。尽管可能会造成混淆,但大部分Web应用程序框架都会创建HTML表单标签,通过<form>标签中的method=POST参数提供数据,随后表单数据会以附件形式包含在请求中。

深入研究函数式视图

HTTP响应和请求都有独立于主体的报头。请求还可以包含一些附加的表单数据或者其他上传的文件,因此可以把一个Web服务器视为如下形式:

headers, content = httpd(headers, request, [form or uploads])

请求报头可能包含cookie值,可以将其视为额外添加的参数。此外,Web服务器通常依赖所运行的操作系统环境,因此也可以将该操作系统环境数据看作请求中提供的附加参数。

对于内容,有一个范围虽广但十分合理的定义。MIME(multipurpose internet mail extensions,多用途互联网邮件扩展)类型定义了Web服务可能返回的内容类型。其中包括纯文本、HTML、JSON、XML,或者网站提供的非文本形式的各种媒体。

当深入研究针对HTTP请求构建响应所需的处理时,会发现一些我们希望复用的公共特性。可复用元素的思想是创建从简单到复杂的Web服务框架。函数式设计的方式允许我们复用函数,这表明函数式方法有助于构建Web服务。

下面介绍如何为服务响应中的各个元素创建管道,来讲解Web服务的函数式设计。我们将通过嵌套负责处理请求的函数来实现这一点,这样外部元素产生的一般开销就不会影响内部元素了。这也使得可以把外部元素作为过滤器,为无效的请求生成错误响应,并让内部函数只关注应用程序本身的处理。

嵌套服务

可以将Web请求处理看作许多嵌套的上下文。例如外层上下文可能涉及会话管理,即检查请求以确定这是现有会话还是新会话中的一个请求;内层上下文可能会为用于检测CSRF(Cross-Site Request Forgeries,跨站请求伪造)的表单处理提供令牌;其他上下文可能会处理会话中的用户身份认证。

对于上面解释的功能,其概念性的视图如下所示:

response = content(
    authentication(
        csrf(
            session(headers, request, forms)
        )
    )
)

这里的思想是每个函数都可以建立在前一个函数的结果之上。每个函数既可以扩充请求,也可以因请求无效而将其拒绝。例如函数session()可以使用报头来确定这是一个现有会话还是一个新会话。由于CSRF处理要求会话有效,因此函数csrf()会检查表单输入以确保使用的是合适的令牌。对于缺少有效凭证的会话,函数authentication()会返回一个错误响应,而当存在有效凭证时,它可以使用用户信息来扩充请求。

函数content()无须担心会话、伪造和未经身份认证的用户。它可以专注于解析路径,以确定应当提供哪类内容。在更复杂的应用程序中,函数content()可能包含极其复杂的映射,用于将路径元素映射到能确定合适内容的函数。

然而嵌套的函数视图仍然不够准确,其问题在于每个嵌套的上下文可能还需要调整响应,而不是或者不仅是调整请求。

理想的情况如下所示:

def session(headers, request, forms):
    pre-process: determine session
    content = csrf(headers, request, forms)
     post-processes the content
     return the content

def csrf(headers, request, forms):
    pre-process: validate csrf tokens
    content = authenticate(headers, request, forms)
    post-processes the content
    return the content

这一概念体现出一种函数式设计思想:可以使用支持扩充输入或输出的嵌套函数集合来创建Web内容。更巧妙的做法是,应当定义可供不同函数使用的简单标准接口。一旦将该接口标准化了,就能以不同的方式组合函数并添加功能了,从而实现函数式编程的目标,用简单明了的程序提供Web内容。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程