超出Symfony路线的范围

kli*_*ach 5 routing symfony

我有一个Symfony控制器的通用结构(使用FOSRestBundle)

/**
 * @Route\Get("users/{id}", requirements={"userId" = "(\d+)"})
 */
public function getUserAction(User $user)
{
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我要求http://localhost/users/1一切都很好.但如果我要求http://localhost/users/11111111111111111我得到500错误和异常

ERROR:  value \"11111111111111111\" is out of range for type integer"
Run Code Online (Sandbox Code Playgroud)

有没有办法在ID传输到数据库之前检查?

作为解决方案,我可以指定id的长度

/**
 * @Route\Get("users/{id}", requirements={"userId" = "(\d{,10})"})
 */
Run Code Online (Sandbox Code Playgroud)

但是Symfony会说没有这样的路线,而不是表明id不正确.

Pao*_*fan 0

By telling Symfony that the getUserAction() argument is a User instance, it will take for granted that the {id} url parameter must be matched to the as primary key, handing it over to the Doctrine ParamConverter to fetch the corresponding User.

There are at least two workarounds.

1. Use the ParamConverter repository_method config

In the controller function's comment, we can add the @ParamConverter annotation and tell it to use the repository_method option.

这样,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);
    }
}        
Run Code Online (Sandbox Code Playgroud)

这只是一个例子:在抛出异常之前我们可以做任何我们喜欢的事情(例如记录失败的尝试)。

然后,在我们的控制器中,添加注释ParamConverter

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
Run Code Online (Sandbox Code Playgroud)

并修改函数注释添加注释:

@ParamConverter("id", class="App:User", options={"repository_method" = "findSafeById"}) 
Run Code Online (Sandbox Code Playgroud)

我们的控制器函数应该如下所示:

  /**
   * @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
  }
Run Code Online (Sandbox Code Playgroud)

此技术允许自定义异常,但无法让您控制响应 - 在生产中您仍然会收到 500 错误。

文档:请参阅此处

2.“老路”解析路线

这种方式是 Symfony 3 之前唯一可行的方式,并且可以让您对生成的响应进行更细粒度的控制。

让我们像这样改变动作原型:

/**
 * @Route\Get("users/{id}", requirements={"id" = "(\d+)"})
 */
public function getUserAction($id)
{
}
Run Code Online (Sandbox Code Playgroud)

现在,在操作中我们将收到请求$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);
      }
    }
Run Code Online (Sandbox Code Playgroud)