2014-04-22 29 views
3

我有一个控制器,它处理来自AJAX请求的表单提交。我不想重复自己,所以我把表单处理代码的方法:使用PUT而不是POST提交表单

// Should process POST request 
public function create(Request $request) 
{ 
    return $this->processEdit($request); 
} 

// Should process PUT request 
public function update($id, Request $request) 
{ 
    $entity = $this->findEntity($id); // custom method 

    if (!$entity) 
     return $this->myCustomErrorResponse(); 

    return $this->processEdit($request, $entity); 
} 

private function processEdit(Request $request, Entity $entity = null) 
{ 
    $form = $this->createForm('my_entity', $entity); 

    $form->handleRequest($request); 

    if ($form->isValid()) { 
     // Do something 
    } else { 
     // Handle invalid form 
    } 

    return $response; 
} 

我有以下两种途径:

ajax_create: 
    pattern:/
    defaults: { _controller: 'MyBundle:Ajax:create' } 
    methods: [ POST ] 

ajax_update: 
    pattern: /{id} 
    defaults: { _controller: 'MyBundle:Ajax:update' } 
    methods: [ PUT ] 
    requirements: 
     id: \d+ 

然而,当我通过AJAX提交表单,它不会接受PUT请求,如果没有任何表单错误消息,返回表单无效。如果我改变控制器代码升技,

$form = $this->createForm('my_entity', $entity, array(
    'method' => 'PUT', 
)); 

...它将处理PUT请求,但不POST请求。

我想知道Symfony2的哪个部分检查表单的HTTP方法,所以我试图在源代码中寻找答案,但是我找不到线索。请问你们有没有分享你的知识?

另一个问题,有没有办法绕过HTTP方法检查?我正在通过$method到上面显示的方法。

非常感谢。


更新:

为了使我的问题更清楚,我的Symfony2应用程序请求路由(POST和PUT)到正确的控制器方法。

我提到的改变上面的代码,那就是:

// Should process POST request 
public function create(Request $request) 
{ 
    return $this->processEdit($request); 
} 

// Should process PUT request 
public function update($id, Request $request) 
{ 
    $entity = $this->findEntity($id); // custom method 

    if (!$entity) 
     return $this->myCustomErrorResponse(); 

    return $this->processEdit($request, 'PUT', $entity); 
} 

private function processEdit(Request $request, $method = 'POST', Entity $entity = null) 
{ 
    $form = $this->createForm('my_entity', $entity, array(
     'method' => $method, 
    )); 

    $form->handleRequest($request); 

    if ($form->isValid()) { 
     // Do something 
    } else { 
     // Handle invalid form 
    } 

    return $response; 
} 

回答

3

只是一对夫妇的(希望)有用的笔记:

首先第一件事情,你可以提交方法从Request对象中,不需要单独传递:

getMethod()

其次,我想我找到了你正在寻找的代码的一部分。首先,如果您在Symfony Form类中检查handleRequest呼叫,您可以看到它从位于配置中的RequestHandler类(请参阅FormConfigInterface类)中调用handleRequest。我猜测RequestHandlerInterface的正确实施是NativeRequestHandler。您可以在线路48上看到检查请求方法是否相同。

现在,为了处理这个问题,您可以将窗体的FormConfigInterface设置为自定义值,在该自定义值中将RequestHandler制作为自己的实现。如果NativeRequestHandler被定义为服务,那么你很幸运(目前我无法访问服务列表)。只需将类切换为指向您自己的实现。

说了这么多,我觉得表单类型检查是有原因的。你应该像现在一样分开处理你的表单提交类型。另外,使用POST插入编辑是一个很好的解决方案。越简单越好,引入新bug的机会就越小!

+0

嘿,那里。昨天我正在执行另一个类似的表单,我突然意识到我可以使用'Request :: getMethod()'!无论如何,非常感谢。 – pikachu0

3

[编辑2014年5月23日]我已经完全修改了我的第一个答案,因为它是一个“肮脏的黑客”。

我有完全相同的问题(和几乎相同的代码)。我在这里阅读了答案,并在自己的代码中发现了一个主要问题,我忘记修改/web/app.php文件以默认启用HttpMethodParameterOverride参数。 (It's a change introduced in Symfony2.2

现在一切正常使用handleRequest()功能预期:

  • 的创建操作使用POST查询。
  • 编辑操作使用PUT查询。
  • 删除操作使用DELETE查询。

我不需要修改接受答案中建议的RequestHandler配置。

现在的代码如下所示:

/** 
* Fruits CRUD service controller. 
* 
* @Route("/services/fruits") 
*/ 
class FruitsController extends Controller 
{ 
    // ... 

/** 
* Create a fruit. 
* 
* @param Request $request 
* 
* @Rest\Post("", name="backend_fruits_create") 
* 
* @return View|array 
*/ 
public function createAction(Request $request) 
{ 
    return $this->processForm($request, new Fruit()); 
} 

/** 
* Edit a fruit. 
* 
* @param Request $request 
* @param Fruit $fruit 
* 
* @Rest\Put("/{id}", name="backend_fruits_edit", requirements={"id" = "\d+"}) 
* @throws HttpException 
* 
* ## DEV FORM ## 
* @Rest\Get("/edit/{id}", name="backend_fruits_edit_dev", requirements={"id" = "\d+"}) 
* @Rest\View 
* ## DEV FORM ## 
* 
* @return View|array 
*/ 
public function editAction(Request $request, Fruit $fruit) 
{ 
    return $this->processForm($request, $fruit); 
} 

/** 
* Delete a fruit. 
* 
* @param Fruit $fruit 
* 
* @Rest\Delete("/{id}", name="backend_fruits_delete") 
* @throws HttpException 
* 
* @return View 
*/ 
public function deleteAction(Fruit $fruit) 
{ 
    $fruit->delete(); 

    return $this->responseHelper->createSuccessResponse(
     $fruit->getTree()->getFruits(), 
     Response::HTTP_ACCEPTED 
    ); 
} 

/** 
* Form handling. 
* 
* @param Request $request 
* @param Fruit $fruit 
* 
* @return View|array 
*/ 
protected function processForm(Request $request, Fruit $fruit) 
{ 
    list($statusCode, $httpMethod, $action) = $this->getActionParameters($fruit); 

    $form = $this->createForm(
     new FruitType(), $fruit, 
     array('action' => $action, 'method' => $httpMethod) 
    ); 

    if (in_array($request->getMethod(), array('POST', 'PUT'))) { 
     if (!$form->handleRequest($request)->isValid()) { 

      return $this->responseHelper->createErrorResponse($form); 
     } 
     $form->getData()->save(); 

     return $this->responseHelper->createSuccessResponse($form->getData(), $statusCode); 
    } 

    return compact('form'); 
} 

/** 
* Set the form and action parameters depending on the REST action. 
* 
* @param Fruit $fruit 
* 
* @return array 
*/ 
protected function getActionParameters(Fruit $fruit) 
{ 
    if ($fruit->isNew()) { 
     $statusCode = Response::HTTP_CREATED; 
     $httpMethod = 'POST'; 
     $action = $this->generateUrl('backend_fruits_create'); 
    } else { 
     $statusCode = Response::HTTP_OK; 
     $httpMethod = 'PUT'; 
     $action = $this->generateUrl('backend_fruits_edit', array('id' => $fruit->getId())); 
    } 

    return array($statusCode, $httpMethod, $action); 
} 

:表单类型绑定到模型实体。

注2:正如你所看到的,我有一个额外的GET路线。开发时,我可以在浏览器中调试我的表单,这非常有用。作为一名服务处理员,我将在确定时删除相关的代码;路线和processForm函数中,不需要测试方法并返回表单。

/** 
* Form handling. 
* 
* @param Request $request 
* @param Fruit $fruit 
* 
* @return mixed 
*/ 
protected function processForm(Request $request, Fruit $fruit) 
{ 
    list($statusCode, $httpMethod, $action) = $this->getActionParameters($fruit); 

    $form = $this->createForm(
     new FruitType(), $fruit, 
     array('action' => $action, 'method' => $httpMethod) 
    ); 

    if (!$form->handleRequest($request)->isValid()) { 
     return $this->responseHelper->createErrorResponse($form); 
    } 
    $form->getData()->save(); 

    return $this->responseHelper->createSuccessResponse($form->getData(), $statusCode); 
} 

注3:响应帮手只是创建与FOSRestBundle定制View响应对象。

更多关于REST和Symfony2中:

+0

对不起,这么晚回复。该项目已进入下一步,一旦我有时间重构旧代码,我将测试您的解决方案。尽量做到尽快!谢谢。 – pikachu0

+0

嗨,你的答案看起来不错,但我找到了一个更好的方式来处理这个问题。看到明显的答案。谢谢。 – pikachu0

相关问题