API平台:如何验证参数?

e5njpo68  于 2022-10-22  发布在  PHP
关注(0)|答案(3)|浏览(145)

我是API Platform的新手,我需要验证路由的ID参数,以验证它是否是Symfony/API Platform应用程序上的整数。
当我查询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

我还尝试过在Customer实体上使用Asserts,但也没有成功。
当我使用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         
 ------- --------------------------------------------------------------------------- ----------

我错过了什么?

6l7fqoea

6l7fqoea1#

你应该签入你的方法:

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

lf3rwulv2#

由于我只需要处理一个特定的错误案例,所以我找到的解决方案是实现一个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 App\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

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行为是我所期望的,如下所示:
|示例请求| HTTP代码结果|
| ------------ | ------------ |
|获取/api/customers/20|200-确定|
|GET /api/customers/15000 | 404-未找到(数据库中不存在此记录)|
|GET /api/customers/abc|400-错误请求|
|GET /api/customers/-1.8|400-错误请求|
如果有人有其他方法可以达到同样的效果,但用一种更干净的方法,请毫不犹豫地提出建议!

wkyowqbh

wkyowqbh3#

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

相关问题