我正在尝试使用Symfony选民和控制器注释来允许或限制对Symfony 4应用程序中某些操作的访问。
例如,“我的前端”提供了删除“帖子”的功能,但前提是用户为该帖子设置了“
前端将HTTP“ DELETE”操作发送到我的symfony端点,并在URL(即/api/post/delete/19
)中传递帖子的ID。
我正在尝试使用here中所述的@IsGranted
注释。
这是我的symfony终结点:
/**
* @Route("/delete/{id}")
* @Method("DELETE")
* @IsGranted("DELETE_POST", subject="post")
*/
public function deletePost($post) {
... some logic to delete post
return new Response("Deleting " . $post->getId());
}
这是我的选民:
class PostVoter extends Voter {
private $attributes = array(
"VIEW_POST", "EDIT_POST", "DELETE_POST", "CREATE_POST"
);
protected function supports($attribute, $subject) {
return in_array($attribute, $this->attributes, true) && $subject instanceof Post;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) {
... logic to figure out if user has permissions.
return $check;
}
}
我遇到的问题是前端只是将资源ID发送到端点。然后,Symfony通过调用选民并传递属性@IsGranted
和帖子ID来解析"DELETE_POST"
注释。
问题是,$ post只是一个帖子ID,而不是实际的Post对象。因此,当选民到达$subject instanceof Post
时,它将返回false
。
我尝试通过将方法签名更改为Post
将public function deletePost(Post $post)
注入我的控制器方法。当然,这是行不通的,因为javascript是在URL中发送ID,而不是Post对象。
(顺便说一句:我知道这种注射方式应该适用于Doctrine,但我不是使用Doctrine)。
我的问题是我如何得到@IsGranted
来理解“发布”应该是发布对象?有没有办法告诉它从传入的ID并根据该ID进行评估来查找Post?甚至是根据另一种控制器方法来确定subject="post"
应该代表什么?
谢谢。
更新
由于@NicolasB,我添加了一个ParamConverter:
class PostConverter implements ParamConverterInterface {
private $dao;
public function __construct(MySqlPostDAO $dao) {
$this->dao = $dao;
}
public function apply(Request $request, ParamConverter $configuration) {
$name = $configuration->getName();
$object = $this->dao->getById($request->get("id"));
if (!$object) {
throw new NotFoundHttpException("Post not found!");
}
$request->attributes->set($name, $object);
return true;
}
public function supports(ParamConverter $configuration) {
if ($configuration->getClass() === "App\\Model\\Objects\\Post") {
return true;
}
return false;
}
}
这似乎按预期工作。我什至不必使用@ParamConverter批注使其起作用。我必须对控制器进行的唯一其他更改是将路由的方法签名更改为public function deletePost(Post $post)
(就像我之前尝试过的-但现在由于我的PostConverter
而起作用)。
我的最后两个问题是:
我应该在supports()
方法中检查什么?我目前正在检查类是否匹配。我是否还应该检查$configuration->getName() == "id"
,以确保使用正确的字段?
如何使它变得更通用?我是否假设您每次在控制器方法中注入实体,Symfony都会在实现supports
的所有对象上调用ParamConverterInterface
方法?
谢谢。
答案 0 :(得分:2)
如果您使用Doctrine将会发生什么,那就是您需要键入提示$post
变量。完成之后,Doctrine的ParamConverter将负责其余的工作。目前,Symfony不知道如何将id
url占位符与$post
参数相关联,因为它不知道实体$post
是指哪个实体。通过使用类似public function deletePost(Post $post)
的类型进行提示并使用ParamConverter,Symfony将知道$post
引用了Post
实体,其ID来自URL的id
占位符。 / p>
来自文档:
通常,您希望在show()中使用$ id参数。相反,通过创建一个新参数($ post)并用Post类(这是一个Doctrine实体)进行类型提示,ParamConverter会自动查询其$ id属性与{id}值匹配的对象。如果找不到帖子,它也会显示404页面。
然后选民还将知道$post
是什么以及如何对待它。
现在,由于您不使用Doctrine,因此默认情况下没有ParamConverter,正如我们刚刚看到的,这是这里的关键要素。因此,您要做的就是简单地定义自己的ParamConverter。
Symfony文档的 This page将告诉您更多有关如何执行此操作的信息,尤其是最后一节“创建转换器”。您将必须告诉它如何使用模型的逻辑将字符串"id"
转换为Post
对象。首先,您可以使其非常特定于Post
对象(并且您可能希望使用converter="name"
选项在注释中显式引用该ParamConverter)。稍后,当您有了可用的版本后,就可以使其更通用。