原文地址:http://owin.org/html/spec/owin-1.0.html,以下为译文。
目录
OWIN: Open Web Server Interface for .NET
OWIN: Open Web Server Interface for .NET
1. 概览
本文档定义了OWIN,它是.NET web服务器与web应用程序之间的一个标准接口。OWIN的目标是解耦服务器与应用程序,并且作为一个开放标准,刺激.NET web开发工具的开源生态系统。
OWIN是根据委托结构定义的。没有OWIN.dll或类似的程序集。实现主机端或应用程序端的OWIN规范不会对项目引入额外的依赖关系。
在本文档中,C# Action/Func语法用于表示某些委托结构,尽管如此,这些委托结构可以使用F#本机函数、CLR接口或者命名委托等效表示。这仅仅因为就是如此设计的。在实现OWIN时,请选择适合您和您的堆栈的委托表示。
本文档中的关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和"OPTIONAL"按照RFC 2119中的描述进行解释。
2. 定义
本文档涉及以下软件角色:
-
服务器 — 直接与客户端通讯并且使用OWIN语义处理请求的HTTP服务器。服务器可能需要一个转换为OWIN语义的适配器层。
-
Web框架 — OWIN之上(原:on top of OWIN)的一个独立组件,它公开了自己的对象模型或API,应用程序可以使用它来促进请求处理。Web框架可能需要一个从OWIN语义转换的适配器层。
-
Web应用程序 — 一个特定的应用程序,它可能构建在一个Web框架之上(原:on top of a Web Framework),使用OWIN兼容的服务器运行。
-
中间件 — 为达到某个特定目的,通过(原:pass through )构成服务器与应用程序之间管道的组件来检查、路由或者修改请求和响应消息。
-
宿主 — 服务器和应用程序在其中运行的进程,主要负责应用程序的启动。有些服务器本身也是宿主。
3. 请求的执行
从广义上讲,服务器调用应用程序(提供一个带有请求和响应的头和体的环境字典作为参数),应用程序要么填充响应,要么指示错误。
3.1. 应用程序委托
OWIN的主要接口被称为“应用程序委托”或者“AppFunc”。 应用程序委托接受一个IDictionary<string, object>类型的环境字典,并且在它结束处理之后返回一个Task。
using AppFunc = Func< IDictionary<string, object>, // Environment Task>; // Done
应用程序最终必须“完成”返回的Task,或者抛出一个异常。
3.2. 环境
环境字典存储请求、响应或者任何相关的服务器状态的信息。 服务器负责在初始化调用中为请求和响应提供标头集合和正文流。随后,应用程序使用响应数据填充相应的字段,填写响应正文,并在完成后返回。
环境字典必须为非null,可变的(注:可增减集合项),并且必须包含下表中所列的必需项。
键的比较必须为
StringComparer.Ordinal。
除非另外指定,键对应的值必须为非null。
除了这些键之外,宿主、服务器、中间件、应用程序等可以添加与请求或响应相关联的任意数据到环境字典中。
另请参见本规范的CommonKeys附录。
3.2.1 请求数据
必需的 | 键名称 | 值描述 |
---|---|---|
是 | owin.RequestBody |
Stream,请求正文流,如果有的话。如果没有请求体,Stream.Null 可以用作占位。请参见请求体。 |
是 | owin.RequestHeaders |
IDictionary<string, string[]> ,请求标头。 |
是 | owin.RequestMethod |
string,HTTP请求方法 (例如: "GET" , "POST" )。 |
是 | owin.RequestPath |
string,请求路径,该路径必须相对于应用程序委托的”root”。 |
是 | owin.RequestPathBase |
string,应用程序委托的“root”对应的请求路径的部分。 |
是 | owin.RequestProtocol |
string,协议名称和版本(例如: "HTTP/1.0" 、 "HTTP/1.1" )。 |
是 | owin.RequestQueryString |
string,HTTP请求URI中的查询字符串,没有前导“?”, (例如: "foo=bar&baz=quux" )。该值可以是空字符串。 |
是 | owin.RequestScheme |
string,请求的URI方案(例如:"http" , "https" )。 |
3.2.2 响应数据
必需的 | 键名称 | 值描述 |
---|---|---|
是 | owin.ResponseBody |
Stream,用于将响应正文写出的流,如果有的话。请参见响应体。 |
是 | owin.ResponseHeaders |
IDictionary<string, string[]> ,响应标头。 |
否 | owin.ResponseStatusCode |
int(可选),在RFC 2616第 6.1.1节中定义的HTTP响应状态码,默认值是200。 |
否 | owin.ResponseReasonPhrase |
string(可选),与给定状态码关联的原因短语。如果没有提供,则服务器应该提供RFC 2616第6.1.1节中描述的默认值。 |
否 | owin.ResponseProtocol |
string(可选),协议名称和版本(例如: "HTTP/1.0" 、 "HTTP/1.1" )。如果没有提供,则"owin.RequestProtocol" 键的值作为默认值。 |
3.2.3 其他数据
必需的 | 键名称 | 值描述 |
---|---|---|
是 | owin.CallCancelled |
CancellationToken ,指示请求是否已取消/终止。 |
是 | owin.Version |
string,指示OWIN版本。 |
3.3. 标头
HTTP请求和响应消息的标头由类型为IDictionary<string, string[]>
的对象表示,以下要求基于RFC 2616第4.2节。
字典必须是可变的。
键必须是不带“
:
”或者空白字符的HTTP字段名称。键的比较必须为
StringComparer.OrdinalIgnoreCase。
键和值字符串中的所有字符都应该在ASCII代码页中。
值数组假定为数据的副本,任何对值数组的预期更改必须通过
headers[headerName] = modifiedArray;
或者headers.Remove(header);
手动保存回标头字典。标头值假定为混合格式,这意味着通常以逗号分隔的标头可能在值数组中显示为单个条目、每个值一个条目或两者的混合(例如:
new string[1] {"value, value, value"}
,new string[3] {"value", "value", "value"}
, ornew string[2] {"value, value", "value"}
)。服务器、应用程序和中介不应该不必要地拆分或合并标头值。虽然这三种格式应该是可互换的,但实际上许多现有实现仅支持一种特定格式。开发人员应该能够灵活地通过生成或使用所选格式来支持现有实现,而不会产生干扰。
3.4. 请求体、100 Continue和完成语义
如果请求指示存在关联的请求体,服务器应该在"owin.RequestBody"
键中提供一个Stream
以访问请求体数据。如果没有预期的请求体数据,Stream.Null
可以用作占位。如果请求的“Expect”标头指示客户端请求“100 Continue”,则由服务器提供此信息。应用程序不得设置"owin.ResponseStatusCode"
为“100”,“100 Continue”只是一个中间响应,这样做会阻止应用程序提供最终响应(例如:200 OK
)。如果应用程序在数据到达之前开始从流中读取,服务器应该代表应用程序发送“100 Continue”。
在结束请求体的处理之前,应用程序委托不应该完成其返回的
Task
,将控制返回到服务器。AppFunc
Task
一旦完成,应用程序不应该继续从请求流中读取。应用程序必须通过“完成其返回的
Task
”或“抛出一个异常”来指示响应体的“完成”或“失败”。Task
完成之后,应用程序不应该向流中再写入任何数据。如果服务器在应用程序委托执行期间通过
"owin.CallCancelled"
发出CancellationToken
信号,应用程序不应该尝试从流中进一步读取,而应该立即完成应用程序委托的Task
。除非应用程序完全消费了请求体,否则它不应该关闭或者销毁给定的流。一旦应用程序委托的
Task
完成,流的所有者(例如:服务器或中间件)必须进行任何必要的清理。任何从请求体的流中抛出的异常都是致命的,应该从
AppFunc
中同步抛出异常或者使异步Task
附加给定的异常而失败,从而返回给服务器。
3.5. 响应体
服务器在初始环境字典中通过"owin.ResponseBody"
提供一个响应体Stream
。在首次向响应体流中写入之前,标头、状态码、原因短语等可以被修改(注:此处并不意味着将上述内容写入响应体流)。在首次向响应体流写入的时候,服务器校验并发送标头。应用程序可以选择缓冲响应数据,以延迟标头终结。
应用程序必须通过完成它返回的
Task
或者抛出一个异常来指示响应体的完成或失败。完成Task之
后,应用程序不应该再向流中写入任何进一步的数据。如果服务器在应用程序委托执行期间通过
"owin.CallCancelled"
发出CancellationToken
信号,应用程序不应该尝试向流中进一步写入,而应该立即完成应用程序委托的Task
。应用程序不应该关闭或者销毁给定的流,因为中间件可能追加额外的数据。一旦应用程序委托的
Task
完成,流的所有者(例如服务器或者中间件)必须执行任何必要的清理。
应用程序不应该假定给定的流支持多个未完成的异步写入。应用程序开发人员应该在尝试使用之前确认服务器和所有在使用的中间件是否支持此模式。
3.6. 请求的生命周期
请求的完整作用域或者生命周期受多个因素的限制,包括客户端、服务器、应用程序委托。以最简单的场景来说,在应用程序委托已完成并且服务器正常结束请求时,请求的生命周期结束。任何级别的故障都可能导致请求提前终止,或者可能在内部处理并允许请求继续。
"owin.CallCancelled"
键与一个CancellationToken
对象相关联,服务器以此标志“请求是否已中止”。如果请求在应用程序委托完成之前发生故障,则应触发此操作。它可以由提供者自由决定在任何点触发。中间件可以用它们自己的token替换该token以提供额外的粒度和功能,但是他们应该将他们的新token与最初提供给它们的token链接起来。
4. 应用程序启动
当宿主进程启动时,它会通过一系列的步骤来设置应用程序。
-
宿主创建一个
IDictionary<string, object>
类型的属性字典,并填充宿主提供的任何启动数据和功能。 -
宿主选择将使用哪个服务器并为其提供属性集合,以便它可以类似地宣告任何功能。
-
宿主找到应用程序的设置代码并使用属性集合调用它。
-
应用程序读取和/或设置属性集合中的配置,构造所需的请求处理管道,并且返回生成的应用程序委托。
-
宿主使用给定的应用程序委托和属性字典调用服务器启动代码。服务器结束自身配置,开始接受请求,并且调用应用程序委托来处理这些请求。
属性字典可用于读取或设置主机、服务器、中间件或应用程序支持的任何配置参数。.
启动属性字典必须是非空、可变,并且必须包含下表中所列的必需键。
键的比较必须为
StringComparer.OrdinalIgnoreCase。
除非另有说明,键的值必须为非空。
必需的 键名称 值描述 是 owin.Version
string
,表示OWIN的版本,在上面的步骤2中由服务器添加,请参见版本管理。
除了这些键之外,宿主、服务器、中间件、应用程序等等都可以将与应用程序配置相关的任意数据添加属性字典中。额外键的指南和常用键的列表可以在本规范的 CommonKeys 附录中找到。
5. URI重建
应用程序通常需要能够重建请求的完整URI。这个过程不会是完美的,因为客户端通常不会传输它们请求的完整URI,但是OWIN为重建请求的近似URI做出了规定。
5.1. URI方案
此信息通常不由HTTP客户端传送,并且根据网络配置,OWIN服务器可能无法确定正确的值。在这些情况下,服务器可能需要手动配置或者计算一个值。
服务器必须提供
"owin.RequestScheme"
的最佳猜测值。
5.2. 主机名
在HTTP/1.1的请求上下文中,客户端所请求的服务器的名称通常表示于请求的Host头字段中,尽管可以使用绝对Request-URI指定 (参见RFC 2616, 5.1.2节, 19.6.1.1节)。
服务器必须为请求头字典中的
"Host"
键提供值。值的格式必须是"<hostname>[:<port>]"
。值应该由主机使用以下步骤推导出来:
- 如果传入请求的Request-URI是一个绝对URI,则
"Host"
键的值必须从绝对URI的主机部分获取。- 如果传入请求的Request-URI不是一个绝对URI,则
"Host"
键的值必须从传入请求的Host头字段中获取。- 如果传入请求中不存在Host头(如在HTTP/1.0请求中),或者其值完全由空白组成,则服务器必须为
"Host"
键提供合理的最佳猜测值。
5.3. 路径
服务器可以将应用程序委托映射到某个基础路径。例如,服务器可能有一个应用程序委托配置为响应以"/my-app"
开头的请求,在这种情况下,它应该将环境字典中"owin.RequestPathBase"
键的值设置为"/my-app"
。如果服务器收到一个"/my-app/foo"
请求,则提供给配置为响应"/my-app"
的应用程序的环境字典的"owin.RequestPath"
值应该是"/foo"
。
环境键
"owin.RequestPathBase"
的值绝不能以斜杠结尾,并且必须以斜杠开头或者为String.Empty
。环境键
"owin.RequestPath"
的值必须以斜杠开头,或者如果"owin.RequestPathBase"
的值不为String.Empty
,则它可以为String.Empty
。
5.4. URI重建算法
以下算法可用于计算当前请求的近似完整URI:
var uri = (string)Environment["owin.RequestScheme"] + "://" + Headers["Host"].First() + (string)Environment["owin.RequestPathBase"] + (string)Environment["owin.RequestPath"]; if(Environment["owin.RequestQueryString"] != "") { uri += "?" + (string)Environment["owin.RequestQueryString"]; }
该算法的结果可能与客户端用于发出请求的URI不同;例如,服务器可能已经进行了一些重写以规范化请求。此外,它受上面 URI方案 和 主机名 部分中描述的警告的影响。
5.5. 百分号编码(Percent-encoding)
URI使用百分号编码来传输其正常允许的字符规则之外的字符(RFC 3986 第二节)。百分号编码用于表示 URI 组件中的基础八位字节, 其八位字节通过UTF-8编码进行解释。大多数Web服务器实现将对路径执行百分号解码以执行请求路由 (请参见:RFC 2616 5.1.2节和 3.2.3节),OWIN遵循此先例。OWIN中的请求查询字符串以其百分号编码形式呈现; 百分号解码的查询字符串可能包含'?'
或'='
字符,这将使字符串不可解析。(注:对于查询字符串“kw=%e6%88%91%3d%e6%88%91”,直接解码为“kw=我=我”,该字符串不可解析。)
服务器必须提供
"owin.RequestPath"
和"owin.RequestPathBase"的
百分号解码值。服务器必须提供
"owin.RequestQueryString"的
百分号编码值。
6. 错误处理
虽然在正常的请求处理场景下存在可以预期的标准异常,例如ArgumentException
、IOException
,但为了构建健壮的服务器或应用程序,仅仅处理此类异常是不够的。如果希望一个服务器是健壮的,它应该始终处理所有从应用程序委托或正文委托中抛出或返回的异常类型。处理机制(例如,记录日志、崩溃并重启、不处理等)取决于服务器和宿主进程。
6.1. 应用程序错误
应用程序可能在以下地方生成异常:
- 从应用程序委托的调用中抛出。
- 作为应用程序委托
Task
的结果。
应用程序应该尝试捕获自己内部的错误,并生成一个合适的响应(可能是500),而不是将异常传播到服务器。
在应用程序提供响应之后,服务器应该在将响应头写入底层传输之前等待从响应Stream
接收至少一次写入。 这样,如果以服务器从应用程序委托Task
获取异常替代对Stream
写入,服务器将仍然能够生成500响应。如果服务器首先获得对Stream
的写入,则它可以安全的假定应用程序已经捕获了它内部尽可能多的错误;服务器可以开始发送响应。如果随后接收到异常,则服务器可以在其认为合适时处理它(例如,记录日志,将错误的文本描述写入底层传输,和/或关闭连接)。
6.2. 服务器错误
当服务器在一个请求的生命周期内遇到错误,它应该向它提供的"owin.CallCancelled"
中的CancellationToken
发出信号。然后,服务器可以采取任何必要的操作以终止请求,但它应该容忍应用程序委托的完成延迟。
7. 版本管理
此标准的未来更新可能包含重大更改(breaking changes)(例如,签名更改、添加或修改键等等)或不间断添加(non-breaking additions)。虽然处理特定更改是标准的更高版本的责任,但以下是可预期的更改的初始准则:
-
标准使用 语义版本管理 (例如,
Major.Minor.Patch
)。 -
对API签名或现有键的重大更改将需要增加主版本号(例如,OWIN 2.0)。
-
以向后兼容的方式添加新键或委托仅需要递增次版本号(例如OWIN 1.1)。
-
仅对文档进行更正和说明只需要增加补丁版本号和最后修改日期(例如OWIN 1.0.1)。
-
在启动属性和请求环境字典中的
"owin.Version"
键表示服务器实现的最新版标准,可用于动态调整应用程序行为。 -
所有的实现者都应该清楚的记录它们支持的OWIN标准的完整版本号。
-
本规范的 CommonKeys附录 中列出的键是严格可选的。 可以在那里添加而不直接影响OWIN标准或者版本号。
*****************转摘:https://blog.csdn.net/xiaoyaocao/article/details/81634758