我正在进行的项目在登录后有一个api。
我使用behat和mink登录:
Scenario: Login
Given I am on "/login/"
And I should see "Login"
When I fill in "_username" with "test"
And I fill in "_password" with "test"
And I press "_submit"
Then I should be on "/"
这有效..
但是,只要我想使用WebApiContext执行以下操作,就不会存储登录会话:
Scenario: Getting the list of pages
When I send a GET request to "/api/pages.json"
Then print response
我在同一个功能中使用这两种方案。我的FeatureContext类看起来像这样:
class FeatureContext extends MinkContext
{
public function __construct(array $parameters)
{
$context = new WebApiContext($parameters['base_url']);
$context->getBrowser()->getClient()->setCookieJar(new \Buzz\Util\CookieJar());
$this->useContext('web', $context);
}
}
我从this issue添加了cookiejar的想法没有成功。当我打印响应时,我只是从登录界面看到HTML页面..
有没有人知道我是否完全走错了方向,或者我是否朝着正确的方向前进?
答案 0 :(得分:2)
我成功使用了相同的方法。我认为没有一种标准的方法可以做到这一点。就您了解cookie基础知识而言,您应该能够实施该解决方案。
在常见情况下,客户端使用某些凭据向服务器发送身份验证请求,服务器验证它,启动经过身份验证的会话并发回具有该会话ID的cookie。以下所有请求都包含该id,因此服务器可以识别被调用者。可以使用特定的头而不是cookie,或者可以使用数据库代替会话,但原理是相同的,您可以(相对)轻松地使用Mink模拟它。
/**
* Start a test session, set the authenticated user and set the client's cookie.
*
* @Given /^I am signed in$/
*/
signIn()
{
session_start();
$_SESSION['user'] = 'jos';
$this->getSession()->getDriver()->setCookie(session_name(), session_id());
session_commit();
}
上面的步骤定义(Behat 3)是它的基础,你手动创建经过身份验证的会话并将其设置为客户端的id。这也必须是另一个例子所说明的内容。
当你开始做更复杂的事情时,PHP的会话可能会有问题,并且这个解决方案有几个大的水下岩石。如果要从两个角度(客户端和服务器)运行断言,您可能经常需要同步会话。这可以通过在所有Mink步骤之前更新cookie并在之后重新加载会话来完成。
/**
* @beforeStep
* @param BeforeStepScope $scope
*/
public function synchroniseClientSession(BeforeStepScope $scope)
{
// Setup session id and Xdebug cookies to synchronise / enable both.
$driver = $this->getSession()->getDriver();
// Cookie must be set for a particular domain.
if ($driver instanceof Selenium2Driver && $driver->getCurrentUrl() === 'data:,') {
$driver->visit($this->getMinkParameter('base_url'));
}
// Also enables the debugging support.
$driver->setCookie(session_name(), session_id());
$driver->setCookie('XDEBUG_SESSION', 'PHPSTORM');
}
/**
* @afterStep
* @param AfterStepScope $scope
*/
public function synchroniseServerSession(AfterStepScope $scope)
{
$driver = $this->getSession()->getDriver();
// Only browser kit driver, only initiated requests, only not repeating requests.
if (!$driver instanceof BrowserKitDriver) {
return;
} elseif (($request = $driver->getClient()->getRequest()) === null) {
return;
} elseif ($request === self::$request) {
return;
}
// Your logic for reloading the session.
self::$request = $request;
}
我遇到的最大问题是会话重新加载。这可能是由于我选择的框架,我怀疑。第一个代码段有session_commit()
,用于保存和关闭会话。理论上,在以下步骤定义中,您必须能够session_id(/* session id from the cookie… */);
和session_start();
,但实际上这不起作用,并且实际上没有从文件加载会话数据,尽管会话确实已开始。为了解决这个问题,我使用session save handler创建了一个使用reload()
方法的自定义会话管理器。
第二个问题是你不能简单地关闭会话而不是编写或破坏会话(支持在PHP 5.6中添加),它依赖于重新加载本身。我用会话管理器的标志重新发明了轮子,告诉它是写还是只是关闭它。
:)