Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
11
respostas

Dúvida no ex. 5 da aula 5: quando tento excluir um produto da lista, está dando erro.

Olá!

Fazendo o exercício 05, a inclusão funcionou normalmente. Mas na opção de excluir, está dando uma falha:

Procedimento que gera o erro: acesso a tela lista... clico em remover... aparece a mensagem abaixo:

HTTP Status 405 -

type Status report

message

description The specified HTTP method is not allowed for the requested resource.

Apache Tomcat/7.0.78

.................................

O início da minha produtoController está assim:

@Controller
public class ProdutoController {

    private final Result result;

    @Inject
    public ProdutoController(Result result) {
        this.result = result;
    }

    @Deprecated
    public ProdutoController() {
        this(null); //para uso do CDI
    }

meu método delete está assim:

@Delete
    public void remove (Produto produto){
        EntityManager em = JPAUtil.criaEntityManager();
        em.getTransaction().begin();
        ProdutoDao dao = new ProdutoDao(em);
        dao.remove(produto);
        em.getTransaction().commit();
        result.forwardTo(this).lista();
    }

e a minha página de lista está: (WEB-INF/jsp/produto/lista.jps)

<tbody>
                <c:forEach items="${produtoList}" var="produto">
                    <tr>
                        <td>${produto.nome}</td>
                        <td>${produto.valor}</td>
                        <td>${produto.quantidade}</td>
                        <td><a href="<c:url value='/produto/remove?produto.id=${produto.id}'/>">Remover</a>
                        </td>
                    </tr>
                </c:forEach>
            </tbody>

o que está acontecendo? por favor, alguém pode me ajudar?

11 respostas

Oi Marciel, tudo certo?

O erro indica que seu método no controller espera receber uma requisição do tipo Delete, e você está enviando um outro tipo, que nesse caso seria Get (por padrão, quando você clica em um link, uma requisição do tipo Get é disparada).

Como o browser suporta apenas requisições do tipo Get e Post, a solução é utilizar um formulário com Post e indicar para o VRaptor qual o método HTTP que queremos executar. Isso pode ser feito por meio de um parâmetro com nome "_method" na requisição:

<form action="/cliente" method="post">
    <input name="cliente.id" value="5" type="hidden" />
    <button type="submit" name="_method" value="DELETE">remover cliente 5</button>
</form>

(http://www.vraptor.org/pt/docs/controllers-rest/#mtodos-http)


No seu caso, ficaria mais ou menos assim:

<td>
    <form action="/produto/remove">
        <input type="hidden" name="produto.id" value="${produto.id}">
        <button type="submit" name="_method" value="DELETE">Remover</button>
    </form>
</td>

Faz sentido?

Abraço!

Olá Lucas Félix, tudo bem?

Não consegui implementar. utilizei o modelo que me passou, li o link que me passou. O @Delete continua com o mesmo erro.

Minha página lista jsp:
<tbody>
<c:forEach items="${listagem}" var="produto">
    <tr>
        <td>${produto.nome}</td>
        <td>${produto.valor}</td>
        <td>${produto.quantidade}</td>
        <td>
                <form action="/produto/remove">
                    <input type="hidden" name="produto.id" value="${produto.id}">
                    <button type="submit" name="_method" value="DELETE">Remover</button>
                </form>
        </td>
</tr>
</c:forEach>
</tbody>

E na ProdutoController, o método remove:

@Delete
    public void remove (Produto produto){
        dao.remove(produto);
        result.redirectTo(this).lista();
    }

Abraços.

Oi Marciel, tudo bem?

Mil desculpas pela demora, eu quis testar em uma aplicação aqui, pra ter certeza que funciona.

No se caso, acho que ficou faltando utilizar o method=post no form, pois o padrão do formulário é enviar uma requisição GET.

Tenta fazer o seguinte:

<form method="post" action="/produto/remove">

e vê se funciona? abraço!

Olá Lucas Félix!

Agora mudou o erro, era o 405 agora está mostrando o 404:

HTTP Status 404 - /produto/remove

type Status report

message /produto/remove

description The requested resource is not available.

Apache Tomcat/7.0.78

Pelo que entendi, o caminho está certo... parece que não está reconhecendo o @Delete... pq quando mudei pra @Get voltou a funcionar.

Não estou entendo quando e como usar esse @Delete. Pelo que está escrito no documento, é pra quando utilizado operações de remoção de um registro do banco de dados, é isso mesmo? (e o PUT para alteração).

Se eu usar o @Get é uma prática ruim? torna o sistema mais fraco?

Segue como está minha produto/lista.jsp

            <tbody>
                <c:forEach items="${listagem}" var="produto">
                    <tr>
                        <td>${produto.nome}</td>
                        <td>${produto.valor}</td>
                        <td>${produto.quantidade}</td>
                        <td>
                            <form method="post" action="/produto/remove">
                                <input type="hidden" name="produto.id" value="${produto.id}">
                                <button type="submit" name="_method" value="DELETE">Remover</button>
                            </form>
                        </td>
                        <td><a href="<c:url value='/produto/remove?produto.id=${produto.id}' />">Remover</a>
                        </td>
                    </tr>
                </c:forEach>
            </tbody>
        </table>
        </div>
        <c:if test="${not empty mensagem}">
            <div class="alert alert-success">${mensagem}</div>
        </c:if>
        <a href="<c:url value='/produto/formulario'/>"> adicione mais produtos</a></br>
        <a href="<c:url value='/produto/listaXML'  />"> veja listaXML </a>
</body>

E a minha ProdutoController:

package br.com.caelum.vraptor.controller;
...
@Controller
public class ProdutoController {

    private final Result result;
    private final ProdutoDao dao;
    private final Validator validator;

    @Inject
    public ProdutoController(Result result, ProdutoDao dao, Validator validator) {
        this.result = result;
        this.dao = dao;
        this.validator = validator;
    }

    @Deprecated
    ProdutoController() {
        this(null,null,null); //para uso do CDI
    }



    @Path("/")
    public void inicio(){

    }

    @Get
    public void lista(){
        result.include("listagem", dao.lista());

    }

    @Get
    public void listaXML(){
        result.use(Results.xml()).from(dao.lista()).serialize();    
    }

    @Get
    public void sobre(){

    }

    @Get
    public void formulario(){

    }

    @Post
    public void adiciona(@Valid Produto produto){

        /*
        validator.check(produto.getQuantidade() > 0, 
                new I18nMessage("erro","produto.quantidade.negativa"));


        */
        validator.onErrorUsePageOf(this).formulario();
        dao.adiciona(produto);
        result.include("mensagem", "Produto cadastrado com sucesso");
        result.redirectTo(this).lista();

    }

    @Delete
    public void remove (Produto produto){
        dao.remove(produto);
        result.redirectTo(this).lista();
    }

}

Desde já agradeço por estar me ajudando!

Grande abraço.

Oi Marciel,

Acho que faltou você por o contexto. Exemplo, aqui eu acesso localhost:8080/teste-deleta/index/form.

No seu caso, dependendo de como você acessa o form, tem que por qual o valor depois do "8080".

Tenta aí alterar a action do form?

Abraço!

Olá,

@Lucas: o contexto do form acredito que esteja certo, pois desse jeito funciona quando altero para o método @Get.

ProdutoController

    @Delete("/produto/remove")
    public void remove (Produto produto){
        dao.remove(produto);
        result.redirectTo(this).lista();
    }

@Ricardo, fazendo nesse jeito que você coloco no link, mas no meu caso, ficou assim:

                        <td>
                            <form method="POST" action="/produto/remove">
                                <input type="hidden" name="produto.id" value="${produto.id}">
                                <input type="hidden" name="_method" value="Delete"/>
                                <input type="submit" class="btn waves-effect waves-light btn-large right" value="REMOVER"></input>
                            </form>
                        </td>

na URL ficou assim:

http://localhost:8080/produto/remove

e me retornou a mensagem 404:

HTTP Status 404 - /produto/remove

type Status report

message /produto/remove

description The requested resource is not available.

Apache Tomcat/7.0.78

.....................

Tentei também desse jeito:

<td><a href="<c:url value='/produto/remove?produto.id=${produto.id}' />">Remover</a>
</td>

e está indo assim na URL:

http://localhost:8080/vraptor-produtos/produto/remove?produto.id=3

E dá a mensagem 405:

HTTP Status 405 -

type Status report

message

description The specified HTTP method is not allowed for the requested resource.

Apache Tomcat/7.0.78

................

Vocês podem me passar o exemplo de um programa de vocês que esteja funcionando para eu ver o que está diferente do meu? pois a principio está certo, está respeitando as convenções.... estou passando o caminho e os parâmetros certo.... bem como seguindo o exemplo dos seus, e de alguns da internet que estou procurando aqui com o @Delete.

Abraços.

os browsers não suportam o método DELETE, vc deve submeter um POST e junto com o post, vc deve enviar tambem o id do objeto que quer remover e um parametro adicional, que no caso do vraptor é o _method

<form action="<c:url value='/produto/remove'/>" method="POST">
      <input type="hidden" name="produto.id" value="${produto.produtoId}"/>
    <input type="hidden" name="_method" value="delete"/>
                <input type="submit" class="btn waves-effect waves-light btn-large right" value="REMOVER"></input>

        </form>

e na sua action, usar a anotação @Delete

@Delete("/produto/remove")
public void remove(Produto produto) {
        em.getTransaction().begin();
        em.remove(busca(produto));
        em.getTransaction().commit();
    }

ao que me lembro quando vc digita a action hardcoded vc precisa colocar antes o contexto da aplicação, então no seu caso, deveria ficar assim

<form method="POST" action="nome_da_aplicacao/produto/remove">

ou então

<form method="POST" action="${request.contextPath}/produto/remove">

por isso eu uso o c:url do jstl, ele já inclui automaticamente o contexto da aplicação

action="<c:url value='/produto/remove'/>"

uma outra forma de fazer seria utilizando o LinkTo, que também já fornece esse contexto

solução!

Oi Marciel!

É basicamente isso que o Ricardo falou. :)

Aqui o exemplo que usei pra testar o que te falei:

https://github.com/lucas-felix/teste-delete

Normalmente a aplicação tem um contexto, exemplo:

http://localhost:8080/projeto-vraptor/alguma-coisa

Quando você tentou usar o c:url, é esse o caminho. Mas para utilizar o método DELETE, por exemplo, você tem que mandar uma requisição POST (isso se deve ao fato de os navegadores atualmente só suportarem GET e POST), por isso o formulário. A tag <a> vai mandar uma requisição GET por padrão, por isso não tá rolando.

Faz sentido?

Abraço!

Valeu galera, agora deu certo.

Desculpe a demora em responder, estava viajando e voltei agora.

No action eu tava fazendo confusão com o caminho, e precisava da barra no inicio do nome. Pois sem a barra a URL ficava duplicando o caminho.

Conforme está no exemplo que o Lucas passou, para o meu caso ficou assim:

<form method="POST" action="/vraptor-produtos/produto/remove">

Fiz aqui e deu certo e agora está certinho.

<form method="POST" action="/vraptor-produtos/produto/remove">
    <input type="hidden" name="produto.id" value="${produto.id}">
    <button type="submit" name="_method" value="DELETE">Remover</button>                            
</form>

Valeuzão Lucas Félix e Ricardo Johannsen. Muito obrigado mesmo. Me ajudaram muito. Muito legal aqui no Alura poder contar com ajuda de professores pacientes e dedicados.

Entendi agora como funciona o @Delete e como implementar.

Grande abraço.