Solucionado (ver solução)
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.