Asp.Net MVC 3, HTML.BeginForm e muita dor de cabeça

Olá!!! Estou de volta para compartilhar mais um aprendizado com o mundo. Faz tempo que não escrevo, pois acho que só vale a pena escrever algo quando não encontro similar no Google (o que convenhamos é raro).

Dessa vez o perrengue que passei está relacionado com o envio de formulários utilizando asp.net mvc 3, mas pelo que entendi o problema pode ocorrer com qualquer linguagem/framework MVC.

O problema era daquele tipo que não ocorre na sua máquina durante o desenvolvimento, mas somente quando se efetua o deploy no servidor de hospedagem (no meu caso na Locaweb). A causa do problema permanece uma incógnita pra mim, e se alguém vier a saber qual é, favor me avisar. O cenário do meu terror era o seguinte:

  1. Formulário gerado via scaffolding (automaticamente) pronto pra ser preenchido no navegador.
  2. O formulário é preenchido corretamente e em seguida é enviado via POST.
  3. O formulário é recarregado com os campos em branco sem que o código do método responsável pela action seja chamado.
  4. Outros formulários da mesma aplicação funcionam normalmente.
  5. Erro só ocorre quando o site é publicado (no meu caso na Locaweb).

Seguindo a (minha) lógica, a primeira coisa a investigar é o que há de diferente entre este formulário e os outros formulários da aplicação que funcionam normalmente. As diferenças encontradas foram as seguintes:

  1. O formulário problemático possui o atributo enctype diferente dos outros, pois o mesmo efetua o upload de arquivos.
  2. Por este motivo o método HTML.BeginForm utilizado na construção do formulário era de uma versão diferente (overload) do que foi utilizado nos outros formulários.

Fora isso tudo era similar. Algumas coisas que cheguei a investigar, mas que não tinham nada a ver com o problema foram:

  1.  Problema com a validação cliente (javascript) do formulário.
  2. Configuração (web.config) do site. Algo relacionado ao upload de arquivos.
  3. Outras coisas que nem me lembro mais.

Seguindo o próximo passo lógico, removi o código diferente usado nesse formulário do arquivo cshtml e do controller responsável pelo mesmo, comparando o html gerado. Os formulários que funcionavam utilizavam o método BeginForm sem parâmetros, enquanto que o formulário com problema utilizava um overload aonde era passado o enctype do mesmo.

Como resultado da comparação verifiquei que quando é utilizado o método BeginForm sem parâmetros, o atributo action do formulário é terminado com uma barra “/”.

<form action=”/crazy/Product/Edit/2/” method=”post”>

Na versão com overload essa barra final não é adicionada.

<form action=”/crazy/Product/Edit/2″ method=”post”>

Após ler meio Google atrás de algo que pudesse esclarecer a importância dessa barra (a)final, descobri que quando ela é incluída na url, o servidor busca pelo diretório que aparece antes da barra. Quando a mesma não é incluída o servidor tenta descobrir se o último parâmetro da url é um arquivo ou um diretório e retorna a url encontrada (caso haja) ao navegador. Daí o navegador realiza uma segunda chamada a essa url.

O problema, meus amigos, é que como a maldita barra não era incluída na action do formulário, o mesmo realizava um post no servidor, que (creio eu) identificava o alvo da url como um arquivo, retornando a url ao navegador que na segunda requisição não usava mais o método POST, e sim o GET. Como no meu controller (e provavelmente nos seus também) existem dois métodos com o mesmo nome, um para o GET, que retorna o formulário a ser preenchido, e um para o POST, responsável por tratar o envio dos dados, o navegador acabava solicitando o formulário em branco (GET), ao invés de enviar o conteúdo do mesmo (POST).

Solução gambiosa (POG): dar um jeito de sempre incluir a barra no final da url do atributo action do formulário.

No meu caso abandonei o uso do BeginForm e usei a tag do formulário harded-coded mesmo (só para as views deste controller, já que nos outros formulários não era necessário o atributo enctype e o BeginForm sem parâmetros gera a url da action com a maldita barra no final).

Para o cshtml de inserção (create) ao invés de:

@using (Html.BeginForm(“Create”, “Product”, FormMethod.Post, new { enctype = “multipart/form-data” })) {

Usei:

<form action=”/Product/Create/” enctype=”multipart/form-data” method=”post”>

Para o cshtml de edição ao invés de:

@using (Html.BeginForm(“Edit”, “Product”, FormMethod.Post, new { enctype = “multipart/form-data” })) {

Usei:

<form action=”/Product/Edit/@Model.id/” enctype=”multipart/form-data” method=”post”>

Lembrando de fechar a tag form no lugar correto.

E pronto. Tudo funcionou as mil maravilhas. Entretanto, como disse anteriormente, só falta saber por que no servidor da minha máquina a falta da barra ao final da url não faz diferença, enquanto que no servidor da Locaweb faz. Provavelmente é alguma configuração de roteamento da url, ou algo parecido. Enfim, a quem souber e puder me informar, ficarei muito grato.

É isso aí, por hoje é só, e até a próxima!

One thought on “Asp.Net MVC 3, HTML.BeginForm e muita dor de cabeça

  1. Putz, cabuloso isso heim kkkk… talvez nessa nova versão do mvc4 que é beta eles tenham corrigido esse problema.

Leave a reply to Ricardo Cancel reply