Sinatra 助手伪造请求

问题描述:

Sinatra 网络应用程序中,如何向应用程序发出虚拟请求并将响应正文返回为文本?比如这些路由...

Within a Sinatra web app, how can I make a virtual request to the application and get the response body back as text? For example, these routes...

get('/foo'){ "foo" }
get('/bar'){ "#{spoof_request '/foo'} - bar" }

...当使用网络浏览器请求/bar"时,应该会得到响应foo - bar".

...should result in the response "foo - bar" when requesting "/bar" with the web browser.

我的应用程序有一个表示错误条目的页面,其中包含有关该错误条目的许多详细信息:该错误发生在哪个版本、它的重要性、与之关联的标签、该错误分配给谁等.

My application has a page representing an bug entry, with lots of details about that bug entry: what version was the bug experienced in, how important is it, what tags are associated with it, to whom is the bug assigned, etc.

用户可以交互编辑此页面上的单个数据.使用我的 AJAXFetch jQuery 插件,JavaScript 使用 AJAX 交换页面的只读部分(例如,分配此错误的人员的姓名)以及用于仅编辑该部分的 HTML 部分表单.用户提交表单,AJAX 对该字段的静态版本发出新请求.

The user may edit individual pieces of data on this page interactively. Using my AJAXFetch jQuery plugin, JavaScript uses AJAX to swap out a read-only section of the page (e.g. the name of the person that this bug is assigned to) with an HTML partial form for editing just that section. The user submits the form, and AJAX makes a new request for the static version of that field.

为了 DRY,我想要创建页面的 Haml 视图使用 AJAX 在创建单个静态部分时发出的完全相同的请求.例如:

In order to be DRY, I want the Haml view that creates the page to use the exact same request that AJAX makes when creating the individual static pieces. For example:

#notifications.section
  %h2 Email me if someone...
  .section-body= spoof_request "/partial/notifications/#{@bug.id}"

不太有效的代码

以下定义 spoof_request 的帮助程序在 Sinatra 1.1.2 下工作:

Not-Quite-Working Code

The following helper defining spoof_request worked under Sinatra 1.1.2:

PATH_VARS = %w[ REQUEST_PATH PATH_INFO REQUEST_URI ]
def spoof_request( uri, headers=nil )
  new_env = env.dup 
  PATH_VARS.each{ |k| new_env[k] = uri.to_s } 
  new_env.merge!(headers) if headers
  call( new_env ).last.join 
end

然而,在 Sinatra 1.2.3 下,这不再有效.尽管将每个 PATH_VARS 设置为所需的 URI,call( new_env ) 仍然会导致 Sinatra 处理当前请求的路由,而不是指定路径.(这会导致无限递归,直到堆栈级别最终触底.)

Under Sinatra 1.2.3, however, this no longer works. Despite setting each of the PATH_VARS to the desired URI, the call( new_env ) still causes Sinatra to process the route for the current request, not for the specified path. (This results in infinite recursion until the stack level finally bottoms out.)

这个问题与从 Sinatra 内部调用 Sinatra 不同,因为接受了对那个(旧)问题的回答不会维持用户的会话.

This question differs from Calling Sinatra from within Sinatra because the accepted answer to that (old) question does not maintain the session of the user.

我使用的代码比 Sinatra README 中的答案,但依赖于相同的机制.由于该版本中的错误,我的代码和 README 中的答案都无法在 1.2.3 下运行.两者现在都在 1.2.6 下工作.

The code I was using is more complex than the answer in the Sinatra README, but relied on the same mechanism. Neither my code nor the answer from the README worked under 1.2.3 due to a bug in that version. Both now work under 1.2.6.

这是一个简单的帮助程序的测试用例:

Here's a test case of a simple helper that works:

require 'sinatra'
helpers do
  def local_get(url)
    call(env.merge("PATH_INFO" => url)).last.join
  end
end
get("/foo"){ "foo - #{local_get '/bar'}" }
get("/bar"){ "bar" }

在行动:

phrogz$ curl http://localhost:4567/foo
foo - bar