【问题标题】:API Platform: how to validate parameter?API 平台:如何验证参数?
【发布时间】:2022-10-22 03:15:15
【问题描述】:

我是 API 平台的新手,我需要验证路由的 ID 参数以验证它是否是 Symfony/API 平台应用程序上的整数。

当我查询GET /api/customers/{id} 时,我想检查 {id} 的值,如果它无效则抛出异常。

例如:

GET /api/customers/10 它按预期工作,如果资源不存在,我会收到 HTTP 200 状态代码或 404 Not Found。

GET /api/customers/abcGET /api/customers/-1 返回 404 Not Found 错误,但在这种情况下,我想返回 400 Bad Request 错误。我该怎么做呢?

我关注了documentation,并创建了一个这样的 EventSubscriber:

// src/EventSubscriber/CustomerManager.php

final class CustomerManager implements EventSubscriberInterface
{
    /**
     * @return array[]
     */
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::VIEW => ['checkCustomerId', EventPriorities::PRE_VALIDATE],
        ];
    }

    /**
     * Check the customer ID on GET requests
     *
     * @param ViewEvent $event
     * @return void
     * @throws MalformedIdException
     */
    public function checkCustomerId(ViewEvent $event)
    {
        $customer = $event->getControllerResult();
        if (!$customer instanceof Customer || !$event->getRequest()->isMethodSafe(false)) {
            return;
        }

        $id = $event->getRequest()->query->get('id');
        if (!ctype_digit($id)) {
            throw new MalformedIdException(sprintf('"%s" is not a valid customer ID', $id));
        }
    }
}

我试图改变优先级,但没有任何反应。

我已经创建并注册了我的新异常:

// src/Exception/MalformedIdException.php

namespace App\Exception;

final class MalformedIdException extends \Exception
{
}

api_platform:

# ...

   exception_to_status:
        # The 4 following handlers are registered by default, keep those lines to prevent unexpected side effects
        Symfony\Component\Serializer\Exception\ExceptionInterface: 400 # Use a raw status code (recommended)
        ApiPlatform\Core\Exception\InvalidArgumentException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST
        ApiPlatform\Core\Exception\FilterValidationException: 400
        Doctrine\ORM\OptimisticLockException: 409

        # Validation exception
        ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY

        # Custom mapping
        App\Exception\MalformedIdException: 400

我也尝试过在客户实体上使用断言,但这也不起作用。

当我使用php bin/console debug:event kernel.view 时,一切似乎都正常:

 ------- --------------------------------------------------------------------------- ---------- 
  Order   Callable                                                                    Priority  
 ------- --------------------------------------------------------------------------- ---------- 
  #1      App\EventSubscriber\CustomerManager::checkCustomerId()                      65        
  #2      ApiPlatform\Core\Validator\EventListener\ValidateListener::onKernelView()   64        
  #3      ApiPlatform\Core\EventListener\WriteListener::onKernelView()                32        
  #4      ApiPlatform\Core\EventListener\SerializeListener::onKernelView()            16        
  #5      ApiPlatform\Core\EventListener\RespondListener::onKernelView()              8         
 ------- --------------------------------------------------------------------------- ---------- 

我错过了什么?

【问题讨论】:

    标签: php symfony api-platform.com


    【解决方案1】:

    你应该检查你的方法:

    function yourMethod($id){
      if (!is_int($id)) {
           return http_response_code(400)
      }
    }
    

    【讨论】:

    • 我不明白我应该在哪个文件中实现这个方法?
    • $id = $event->getRequest()->query->get('id'); 之后你可以做这个检查。
    • 我不确定此检查是否遵循与 Symfony/API 平台相关的最佳实践......无论如何,结果与上面解释的结果相同
    • 抱歉,只有在您检查 id 变量的整数时才有这种方法。如果你愿意,你可以创建中间件,你可以这样检查。
    • 它不起作用:如果我在checkCustomerId() 函数中添加var_dump($id); die();,并查询GET /api/customers/12,我得到string(2) "12",这是我在Json 响应中所期望的。但是,如果我使用数据库中不存在或无效的 ID 进行查询,则根本不会使用我的 EventSubscriber,因为我在 Json 响应而不是我的 var_dump 中收到 404 错误...我必须找到另一种方法来做,但我不知道该尝试什么来找到解决方案
    【解决方案2】:

    由于我只需要处理一个特定的错误情况,我找到的解决方案是实现一个 ExceptionListener。

    我使用了the one provided in the Symfony documentation,然后我对其进行了修改,以便它通过以 Json 格式返回所有错误来重现 API 平台行为。

    然后,我有条件地处理了遇到 404 错误并且请求中有 ID 参数的情况,如下所示:

    // If we encountered 404 error and ID param is not valid, send a 400 error instead of 404
    if ($response->isNotFound() && !filter_var($id, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]])) {
        $response->setStatusCode(400);
        $response->setData([
            'message' => 'Bad Request',
            'code'    => Response::HTTP_BAD_REQUEST,
            'traces'  => $exception->getTrace(),
        ]);
    }
    

    这是 ExceptionListener 的完整代码:

    // src/EventListener/ExceptionListener.php
    
    namespace AppEventListener;
    
    use SymfonyComponentHttpFoundationJsonResponse;
    use SymfonyComponentHttpFoundationResponse;
    use SymfonyComponentHttpKernelEventExceptionEvent;
    use SymfonyComponentHttpKernelExceptionHttpExceptionInterface;
    
    class ExceptionListener
    {
        /**
         * @param ExceptionEvent $event
         * @return void
         */
        public function onKernelException(ExceptionEvent $event)
        {
            $exception = $event->getThrowable();
            $request = $event->getRequest();
    
            // Check if request come from REST API :
            if ('application/json' === $request->headers->get('Content-Type')) {
    
                $response = new JsonResponse([
                    'message' => $exception->getMessage(),
                    'code' => $exception->getCode(),
                    'traces' => $exception->getTrace(),
                ]);
    
                if ($exception instanceof HttpExceptionInterface) {
                    $response->setStatusCode($exception->getStatusCode());
                    $response->headers->replace($exception->getHeaders());
    
                    $id = $event->getRequest()->get('id');
    
                    // If we encountered 404 error and ID param is not valid, send a 400 error instead of 404
                    if ($response->isNotFound() && !filter_var($id, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]])) {
                        $response->setStatusCode(400);
                        $response->setData([
                            'message' => 'Bad Request',
                            'code'    => Response::HTTP_BAD_REQUEST,
                            'traces'  => $exception->getTrace(),
                        ]);
                    }
                } else {
                    $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
                }
    
                $event->setResponse($response);
            }
        }
    }
    

    现在 API 行为符合我的预期,如下所示:

    Example request HTTP Code result
    GET /api/customers/20 200 - Ok
    GET /api/customers/15000 404 - Not Found (this record don't exist in DB)
    GET /api/customers/abc 400 - Bad Request
    GET /api/customers/-1.8 400 - Bad Request

    如果有人有其他方法可以达到相同的结果,但以更清洁的方式,请不要犹豫,提出建议!

    【讨论】:

      【解决方案3】:

      如果您查看事件系统 (https://api-platform.com/docs/core/events/) 的文档,则说明 PRE_VALIDATE 挂钩仅支持 (POST、PUT、PATCH) 而不支持 GET。我遇到了同样的问题。我仍在寻找更好的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-23
        • 2020-07-03
        • 2012-04-10
        • 2022-08-05
        • 1970-01-01
        • 2017-08-02
        • 1970-01-01
        相关资源
        最近更新 更多