我正在编写一个在 App Engine 的 Go 运行时上运行的 Go 应用程序。
我注意到几乎所有使用 App Engine 服务(例如 Datastore、Mail 甚至 Capabilities)的操作都需要您向它传递一个appengine.Context
必须使用 function 检索的实例appengine.NewContext(req *http.Request) Context
。
当我为 App Engine 编写这个应用程序时,如果我愿意的话,我希望能够轻松快速地将它移动到其他平台(可能是不支持任何 App Engine API 的平台)。
因此,我通过围绕任何 App-Engine 特定交互(包括请求处理函数)编写小包装器来抽象出与 App Engine 服务和 API 的实际交互。使用这种方法,如果我确实希望迁移到不同的平台,我只需重写那些将我的应用程序与 App Engine 绑定的特定模块。简单明了。
唯一的问题是那个appengine.Context
对象。我无法将它从我的请求处理程序通过我的逻辑层传递到处理这些 API 的模块,而无需将我的所有代码都绑定到 App Engine。我可以传递可以从中派生对象http.Request
的appengine.Context
对象,但这需要耦合可能不应该耦合的事物。(我认为最好的做法是让我的应用程序都不知道它是一个 Web 应用程序,除了那些专门用于处理 HTTP 请求的部分。)
想到的第一个解决方案是在某个模块中创建一个持久变量。像这样的东西:
package context
import (
"appengine"
)
var Context appengine.Context
然后,在我的请求处理程序中,我可以使用和在直接使用 App Engine 服务的模块中设置该变量context.Context = appengine.NewContext(r)
,我可以通过访问来获取上下文context.Context
。没有任何干预代码需要知道appengine.Context
对象的存在。唯一的问题是“给定实例可能同时处理多个请求”,这可能会导致此计划的竞争条件和意外行为。(一个请求设置它,另一个设置它,第一个访问它并获取错误的appengine.Context
对象。)
理论上我可以将数据存储appengine.Context
到数据存储区,但是我必须将一些特定于请求的标识符向下传递到逻辑层到特定于服务的模块,以识别appengine.Context
数据存储区中的哪个对象是当前请求的对象,这将再次结合事物我认为不应该耦合。(而且,它会增加我的应用程序的数据存储使用量。)
我还可以将appengine.Context
对象传递到整个逻辑链中,interface{}
并让任何不需要appengine.Context
对象的模块忽略它。这将避免将我的大部分应用程序与任何特定的. 然而,这似乎也很混乱。
所以,我有点茫然如何干净地确保需要该appengine.Context
对象的 App-Engine 特定模块可以获取它。希望你们能给我一个我还没有想到的解决方案。
提前致谢!