2017-08-23 106 views
1

我的问题涉及json负载验证的各种过程。 我已经接受: - 对模型进行反序列化,调用验证器服务并验证水合对象。 - 使用FormType(即使没有窗体...只是json提要),并在注入$ datas后验证表单构建器。Symfony Api Rest验证器进程

你更喜欢哪一个? 你有更好的解决方案吗?比如,也许一个中间件(唯一捆绑OU应用程序,与所有输入/输出,来有效载荷交易 - 请求/响应)

谢谢

+0

对我来说,最好的选择是通过形式 实体水化所以,你必须动态验证过程调用您的水合实体。 错误处理已经存在等 – Mcsky

回答

1

我验证/与本地听众/工具FOSRestBundle提供反序列化。

利用这个bundle,你可以拥有本地的form-validation ...或者自动反序列化和验证的模型,以及作为控制器参数注入的验证错误列表。

# app/config/config.yml 

# You need SensioFrameworkExtraBundle for body converters to work 
sensio_framework_extra: 
    request: { converters: true } 

fos_rest: 
    zone: 
    - path: '^/api/(.*)+$' 
    # [..] 
    body_listener: 
    enabled: true 
    default_format: json 
    decoders: 
     json: fos_rest.decoder.jsontoform 

    # automatically injects query parameters into controller Actions 
    # see @FOSRest\QueryParam in the example below 
    param_fetcher_listener: force 

    # https://symfony.com/doc/master/bundles/FOSRestBundle/request_body_converter_listener.html 
    body_converter: 
    enabled: true 
    validate: true 
    validation_errors_argument: validationErrors 

主体变换器可以反序列化和为您自动验证模型(不使用任何形式或手动步骤)。例如:

/** 
* @ParamConverter(
* "post", 
* converter = "fos_rest.request_body", 
* options = { 
*  "validator" = { 
*  "groups" = { 
*   "validation-group-one", 
*   "validation-group-two", 
*  } 
*  }, 
*  "deserializationContext" = { 
*  "groups" = { 
*   "serializer-group-one", 
*   "serializer-group-two" 
*  }, 
*  "version"="1.0" 
*  } 
* } 
*) 
*/ 
public function putPostAction(Post $post, ConstraintViolationListInterface $validationErrors) 
{ 
    if (!empty($validationErrors)) { 
     // return some 4xx reponse 
    } 
    // Do something with your deserialized and valid Post model 

捆绑包可以将表单(和表单错误)序列化为JSON。

即具有无效字段的表单将呈现为:

{ 
    "code": 400, 
    "message": "Validation Failed", 
    "errors": { 
     "errors": [ 
      "This is a global form error." 
     ], 
     "children": { 
      "oldPassword": { 
       "errors": [ 
        "The old password is not correct." 
       ] 
      }, 
      "newPassword": [], 
      "submit": [] 
     } 
    } 
} 

FOSRestBundle提供请求体听者自动解码Content-Type: application/jsonContent: application/x-www-form-urlencodedRequest对象内,从而可以绑定请求到窗体具有如同你使用普通的HTML表单一样。您可以使用查询参数(在以下示例中为?validate=true)发送请求,并返回HTTP 200(OK)/ 202(接受)的早期响应)在执行任何业务逻辑之前。

下面的例子表明,接受形式的请求端点:

{ 
    "oldPassword": "xxxxxxx", 
    "newPassword": "yyyyyyy" 
} 

相应的控制器动作:

/** 
* @FOSRest\Route(
* "/profile/change-password", 
* name="api_put_password", 
* methods={ 
*  Request::METHOD_PUT 
* } 
*) 
* 
* @FOSRest\QueryParam(
* name="validate", 
* allowBlank=false, 
* default="false", 
* strict=true, 
* nullable=true, 
* requirements="^(true|false)$" 
*) 
*/ 
public function putPasswordAction(Request $request, string $validate = 'false') 
{ 
    $validate = filter_var($validate, FILTER_VALIDATE_BOOLEAN); 

    $form = $this->formFactory->createNamed(null, ChangePasswordType::class, null, [ 
     'action' => $this->router->generateUrl('api_put_password'), 
     'method' => $request->getMethod(), 
    ]); 

    $form->handleRequest($request); 

    if (!$form->isValid()) { 
     $view = new View(); 
     $view->setStatusCode(Response::HTTP_BAD_REQUEST); 
     $view->setData($form); 

     return $view; 
    } 

    if ($validate) { 
     $view = new View(); 
     $responseCode = Response::HTTP_ACCEPTED; 
     $view->setStatusCode($responseCode); 
     $view->setData([ 
      'code' => $responseCode, 
      'message' => 'Data is valid.', 
      'data' => null 
     ]); 

     return $view; 
    } 

    $user = $this->securityContext->getToken()->getUser(); 
    /** @var PasswordChangeRequest $passwordChangeRequest */ 
    $passwordChangeRequest = $form->getData(); 
    $user->setPassword($this->passwordEncoder->encodePassword($user, $passwordChangeRequest->getNewPassword())); 
    $this->userManager->persist($user); 

    $view = new View(); 
    $view->setStatusCode(Response::HTTP_OK); 
    $view->setData([ 
     'code' => Response::HTTP_OK, 
     'message' => 'Password changed successfully.', 
     'data' => $user 
    ]); 

    $context = new Context(); 
    $context->setGroups([ 
     'profile' 
    ]); 
    $view->setContext($context); 

    return $view; 
} 
+0

我正在寻找这种解决方案nifr,它是完美的!谢谢。 – iKonenn

+0

我有一个paramconverter @nifr的小问题,params添加到控制器动作中,改变url格式,比如“Post $ post”现在包含在url中。你知道我只能对待http_row_post_data和post方法吗?就是说,只处理$ request-> getContent()我的json有效载荷在哪里 – iKonenn

+0

这些似乎是两个不同的问题,对吧?您可以通过明确提供@Route或添加@NoRoute并将该路由的配置直接放入'routing.yml'中来防止控制器参数显示在url中。动作参数通常只添加到带有隐式路由/ URL命名的URL中。你用那个吗?我没有得到第二部分。你想访问控制器动作中的原始JSON负载? – nifr