超出Symfony路线的范围

时间:2018-04-04 12:22:26

标签: symfony routing

我有一个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不正确。

1 个答案:

答案 0 :(得分:0)

通过告诉Symfony getUserAction()参数是User实例,理所当然地认为{id} url参数必须与主键匹配,将其交给Doctrine ParamConverter用于获取相应的User

至少有两种解决方法。

1。使用ParamConverter 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

2。解析路线“旧路”

这种方式是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);
      }
    }