我有一个 Symfony Controller 的通用结构(使用 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 不正确。
最佳答案
通过告诉 Symfony getUserAction()
参数是 User
例如,理所当然地认为 {id}
url参数必须匹配as主键,交给Doctrine ParamConverter去获取对应的User
.
至少有两种解决方法。
1.使用参数转换器repository_method
配置
在 Controller 函数的注释中,我们可以添加 @ParamConverter
注释并告诉它使用 repository_method
选项。
这样,Symfony 会将 url 参数传递给我们实体 中的函数。存储库 ,从中我们将能够检查 url 参数的完整性。
在 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);
}
}
这只是一个例子:我们可以在抛出异常之前做任何我们喜欢的事情(例如记录失败的尝试)。
然后,在我们的 Controller ,让我们包含
ParamConverter
注解:use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
并修改添加注释的功能注释:
@ParamConverter("id", class="App:User", options={"repository_method" = "findSafeById"})
我们的 Controller 函数应该如下所示:
/**
* @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 .
2.解析路线“老路”
这种方式是 Symfony 3 之前唯一可行的方式,并且可以让您对生成的响应进行更细粒度的控制。
让我们像这样更改 Action 原型(prototype):
/**
* @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);
}
}
关于symfony - Symfony 路由中的 ID 超出范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49650926/