我有一个Symfony控制器的常见结构(使用FOSRestBundle)
/**
* @Route\Get("users/{id}", requirements={"userId" = "(\d+)"})
*/
public function getUserAction(User $user)
{
}
现在如果我要求http://localhost/users/1
一切都很好。但是,如果我请求http://localhost/users/11111111111111111
,我会得到500错误和异常
ERROR: value \"11111111111111111\" is out of range for type integer"
有没有办法在将id传输到数据库之前检查它?
作为解决方案,我可以指定id的长度
/**
* @Route\Get("users/{id}", requirements={"userId" = "(\d{,10})"})
*/
然后Symfony会说没有这样的路线,而不是显示id不正确。
答案 0 :(得分:0)
通过告诉Symfony getUserAction()
参数是User
实例,理所当然地认为{id}
url参数必须与主键匹配,将其交给Doctrine ParamConverter用于获取相应的User
。
至少有两种解决方法。
repository_method
config 在控制器功能的注释中,我们可以添加@ParamConverter
注释并告诉它使用repository_method
选项。
这样Symfony会将url参数传递给我们的实体 存储库 中的一个函数,从中我们将能够检查url参数的完整性。< / p>
在UserRepository
中,让我们创建一个按主键获取实体的函数,首先检查参数的完整性。也就是说,$id
不能大于PHP可以处理的最大整数(PHP_INT_MAX
常量)。
请注意:$id
是一个字符串,因此将它与PHP_INT_MAX
进行比较是安全的,因为PHP会自动将PHP_INT_MAX
强制转换为字符串并进行比较它到$id
。如果它是整数,则测试将始终失败(按设计,所有整数都小于或等于PHP_INT_MAX
)。
// ...
use Symfony\Component\Form\Exception\OutOfBoundsException;
class UserRepository extends ...
{
// ...
public function findSafeById($id) {
if ($id > PHP_INT_MAX) {
throw new OutOfBoundsException($id . " is too large to fit in an integer");
}
return $this->find($id);
}
}
这只是一个例子:我们可以在抛出异常之前做任何我们喜欢的事情(例如记录失败的尝试)。
然后,在我们的控制器中,让我们包含ParamConverter
注释:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
并修改添加注释的函数注释:
@ParamConverter("id", class="App:User", options={"repository_method" = "findSafeById"})
我们的控制器功能应如下所示:
/**
* @Get("users/{id}")
* @ParamConverter("id", class="App:User", options={"repository_method" = "findSafeById"})
*/
public function getUserAction(User $user) {
// Return a "OK" response with the content you like
}
此技术允许自定义异常,但不会让您控制响应 - 您仍然会在生产中遇到500错误。
文档:请参阅here。
这种方式是Symfony 3唯一可行的方法,并且可以对生成的响应进行更细粒度的控制。
让我们像这样改变动作原型:
/**
* @Route\Get("users/{id}", requirements={"id" = "(\d+)"})
*/
public function getUserAction($id)
{
}
现在,在操作中我们会收到请求的$id
,我们将能够检查它是否正常。如果没有,我们抛出一个异常和/或返回一些错误响应(我们可以选择HTTP状态代码,格式和其他任何东西)。
您可以在下面找到此过程的示例实现。
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\Form\Exception\OutOfBoundsException;
use Symfony\Component\HttpFoundation\JsonResponse;
class MyRestController extends FOSRestController {
/**
* @Get("users/{id}", requirements={"id" = "(\d+)"})
*/
public function getUserAction($id) {
try {
if ($id > PHP_INT_MAX) {
throw new OutOfBoundsException($id . " is too large to fit in an integer");
}
// Replace App\Entity\User with your actual Entity alias
$user = $this->getDoctrine()->getRepository('App\Entity\User')->find($id);
if (!$user) {
throw new \Doctrine\ORM\NoResultException("User not found");
}
// Return a "OK" response with the content you like
return new JsonResponse(['key' => 123]);
} catch (Exception $e) {
return new JsonResponse(['message' => $e->getMessage()], 400);
}
}