2017-08-23 15 views
1

私の質問は、jsonペイロード検証のさまざまなプロセスに関係しています。 私はrecensed: - モデルの逆シリアル化、バリデーター・サービスの呼び出し、および水和オブジェクトの検証。 - FormTypeを使用する(フォームがない場合でも... jsonフィードのみ)、$ datasを注入した後にフォームビルダを検証します。Symfony Api残りのバリデータプロセス

どちらをお好みですか? より良い解決策をお探しですか?このような多分としてミドルウェア(全てのイン/アウト-てくるペイロードを扱うユニークなバンドルOUアプリ - 要求/応答)

は、私はFOSRestBundleが提供するネイティブのリスナー/ツールを使用してデシリアライズ/検証

+0

あなたの水和した実体の検証プロセスコール。 エラー処理は既に行われています – Mcsky

答えて

1

ありがとうございました。

バンドルを使用すると、ネイティブフォーム検証...または自動的に逆シリアル化され検証されたモデルとコントローラ引数として注入された検証エラーのリストを持つことができます。

# 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は、あなたがフォームにリクエストをバインドすることができますので、自動的にRequestオブジェクト内Content: application/x-www-form-urlencodedContent-Type: application/jsonをデコード要求ボディリスナーを提供通常のHTMLフォームと同様にhandleRequestとなります。

クイックヒント:データを非同期的に検証したい場合は、クエリパラメータ(次の例では?validate=true)でリクエストを送信し、HTTP 200(OK)/ 202(Accepted )を使用してビジネスロジックを実行します。

次の例では、フォームの要求を受け入れるエンドポイントを示しています

{ 
    "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に少し問題があります。コントローラアクションに追加されたパラメータは、 "Post $ post"のようなurl形式をurlに含めました。私はhttp_row_post_dataとpostメソッドだけを扱うことができますか?$ request-> getContent()を処理します。ここでjsonペイロードはどこですか? – iKonenn

+0

これは2つの異なる問題であるようですね。 @Routeを明示的に提供するか@NoRouteを追加し、そのルートの設定を直接 'routing.yml'に入れることで、コントローラ引数がurlに現れるのを防ぐことができます。アクション引数は、通常、暗黙的なルート/ URLの名前付けでURLに追加されます。あなたはそれを使用していますか?私は第二の部分を得ていない。コントローラーアクション内の元のJSONペイロードにアクセスしたいですか? – nifr