symfony - Symfony 路由中的 ID 超出范围

标签 symfony routing

我有一个 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/

相关文章:

asp.net-mvc - 带有默认 Controller 的 ASP.NET MVC 路由

ruby-on-rails - Devise and I18n - 重置密码路由问题

amazon-ec2 - 在创建 AWS 路由表以使子网可公开访问时出现 Terraform "Route target is not supported"

php - 在关联(数组)上找到 Doctrine\Common\Collections\ArrayCollection 类型的实体,但期待(其他)

php - 未捕获的 PHP 异常 Doctrine\ORM\ORMException : "Unknown Entity namespace alias ' AppBundle'.“

symfony - Symfony2 Controller 中的构造函数

c# - ASP.NET MVC 结合 Web API 路由

algorithm - 最少乘坐次数

symfony - Akeneo 2.2.8 : How can I get the original attribute data in the akeneo. storage.pre_save 事件?

symfony - 无法 Autowiring "App\Controller\BlogController::edit()": it references class "App\Entity\Blog" but no such service exists 的参数 $blog