使用Symfony 3进行子请求时遇到奇怪的行为。
我认为解释为什么我会做出导致问题的原因会更好,这可能有助于找到解决方案。 但如果阅读太多,你可以跳过这部分。
我开发了一种Api Platform的克隆,不是一个分叉,而是一个灵感的重写。 我想添加的功能之一是能够从服务器端调用Api。
Api本身做了很多工作(例如关系很好),所以让它做的非常有用 即使对于经典的服务器端应用程序(没有ajax调用),持久化/过滤等工作。
我发现这样做的最简单方法是执行子请求。我当前的实现在使用时看起来像这样:
// To get a collection of articles (output as array)
$this->proxy->get(Article::class)->collection($page, $itemsPerPages, $filters, $ordering)->asArray();
// To get a single article (output as entities)
$this->proxy->get(Article::class)->item('a2Ck2')->asObject();
// To persist an article (output as entities)
$this->proxy->persist(Article::class)->item(['title' => 'Super article'])->asObject();
你明白了。
当您调用as*
方法时,会执行请求,并且执行此操作的代码非常简单:
// Proxy.php
public function execute(Request $request, Response &$response = null)
{
$response = $this->kernel->handle($request);
[...]
// Handle the response status code, the content etc. Not important here.
}
execute
方法的参数中给出的请求是这样构建的(这里显示了GET构建器):
// GetRequestBuilder.php
protected function createRequest(): Request
{
$currentRequest = $this->getCurrentRequest();
$request = Request::create(
$this->generateUri([...]),
Request::METHOD_GET,
[],
$currentRequest->cookies->all()
);
return $request;
}
如果我有多个防火墙,并且当我执行的子请求与不同防火墙中的路由匹配时,会出现问题。
对于以下security
配置:
# app/config/security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
providers:
main:
id: app.main_user_provider
doctrine:
entity:
class: AppBundle:User
property: username
firewalls:
backend:
pattern: ^/admin
anonymous: ~
logout:
path: /admin/logout
target: /admin/login
invalidate_session: true
stateless: false
guard:
entry_point: app.backend.login_form_authenticator
authenticators: [app.backend.login_form_authenticator]
frontend:
pattern: ^/
anonymous: ~
logout:
path: /logout
target: /login
invalidate_session: true
stateless: false
guard:
entry_point: app.frontend.login_form_authenticator
authenticators: [app.frontend.login_form_authenticator]
如果我从管理路由发出子请求到与frontend
防火墙匹配的路由,我将失去管理员会话。
如果我在Api通话之前和之后转储会话,它会给我以下内容:
array (size=3)
'_security.backend.target_path' => string 'http://localhost/admin/api/formation/categories' (length=51)
'_csrf/form' => string 'wt9Js9b8deT00XanUgEq23qXFqY8uHrt_j5i6D9Btj8' (length=43)
'_security_backend' => string 'C:67:"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken":1786:{a:2:{i:0;s:7:"backend";i:1;s:174'... (length=1868)
在子请求之后:
array (size=2)
'_security.backend.target_path' => string 'http://localhost/admin/api/formation/categories' (length=51)
'_csrf/form' => string 'wt9Js9b8deT00XanUgEq23qXFqY8uHrt_j5i6D9Btj8' (length=43)
_security_backend
密钥已消失,我的会话也已消失。
如果我不将原始请求的cookie复制到子请求,会话不会丢失,但如果Api路由受到保护,我会遇到问题:
// GetRequestBuilder.php
protected function createRequest(): Request
{
$currentRequest = $this->getCurrentRequest();
$request = Request::create(
$this->generateUri([...]),
Request::METHOD_GET,
[],
// Removing this line and replace it with an empty array solves the problem of loosing the session.
// But if the target route is behind the "backend" firewall, the sub request will be redirected to the login page.
$currentRequest->cookies->all()
);
return $request;
}
1)您是否认为这种做出子请求的方法足够强大,或者您是否知道获得相同结果的更好方法?
旁注:我不做CURL或任何其他方式来执行真实 http请求,因为我希望能够获得实体(实际对象)是Api调用的结果。
2)如果子请求与具有不同防火墙的路由匹配,您是否知道一种防止原始会话丢失的方法? 正如我现在所理解的那样,我看到的唯一方法是检测(不知道如何......)目标路由是否在同一个防火墙上,如果是这样的话只复制cookie。 似乎很难做到很难稳定。它看起来很脏**。
任何想法都会非常感激。
感谢您的帮助。
答案 0 :(得分:0)
我发布了自己的答案,因为我找到了一种似乎可以处理案件的丑陋方式,但我觉得非常脏,如果有人能想到一个干净的方式来处理我会很高兴这种情况。
所以解决方案是在请求之前“备份”会话数据并在之后恢复...我这样做了:
// Proxy.php
public function execute(Request $request, Response &$response = null)
{
try {
$this->saveSession();
$response = $this->kernel->handle($request);
[...]
// Handle the response status code, the content etc. Not important here.
} finally {
$this->restoreSession();
}
}
/**
* Saves the current session.
*/
private function saveSession()
{
$this->sessionStack[] = (array)$this->session->all();
}
/**
* Restores the latest session saved using saveSession().
*/
private function restoreSession()
{
$data = array_pop($this->sessionStack);
$this->session->clear();
foreach ($data as $k => $v) {
$this->session->set($k, $v);
}
}
当然,我仍然会在子请求中复制当前请求的cookie。
这是非常基本的,但我似乎工作(至少我目前的情况)。 我不知道像这样复制会话数据的副作用,但我猜这是件坏事。
它只能起作用,因为我需要保留_security_backend
键,这是一个序列化对象,因此完全可以“备份”为字符串。但是,如果在将来的情况下会话中包含正在子请求中修改的实际对象,则根本不起作用。
如果会话拥有大量数据,性能如何呢? HMM的
所以,如果你们中的任何一个人有更好的解决方案,我会全力以赴:)