VRaptor 3 / Validação

Olá pessoal

Aqui eu tenho uma validação que fiz e funciona:

JAVA


public class ProjetoController {

	private Result result;
	private ProjetoDao dao = new ProjetoDao();
	private Validator validator;
	
	public ProjetoController(Result result, Validator validator){
		this.result = result;
		this.validator = validator;
	}

        @Path("/projetos")  
        @Post
        public void insert(Projeto projeto){
	        //Validação
	        if(projeto.getNome().equals("")){
		      validator.add(new ValidationMessage("Nome em Branco","nomeEmBranco"));
	        }
	        validator.onErrorUse(Results.page()).of(getClass()).erro();
	        //validator.onErrorUse(Results.logic()).redirectTo(getClass()).list();
		
	        //Inserção e redirecionamento de página
	        dao.insert(projeto);
	        result.use(Results.logic()).redirectTo(getClass()).list();
        }


        public void erro(){
		
	    }
//...
}

erro.jsp

<HTML>
<BODY>
<c:forEach var="error" items="${errors}">  
	${error.message}
</c:forEach>
</BODY>
</HTML>

Porém, eu gostaria de exibir a mensagem de erro no próprio formulário:

list.jsp

<form action="<c:url value="/projetos"/>"/>
	Projeto: <br><input name="projeto.nome"/><br>
	<button type="submit" name="_method" value="POST">SALVAR</button>  
	<button type="submit" name="_method" value="DELETE">DELETAR</button><br>
</form>

Tentei direcionar para o método list() e inserir o código do erro.jsp no list.jsp mas não apareceu nada.

Se alguém puder me ajudar, agradeço desde já!!!
Obrigado.

Se você colocar isso no seu JSP funciona?

[code]<form action="<c:url value="/projetos"/>"/>

  • ${error.message}
 Projeto: <br><input name="projeto.nome"/><br>  
 <button type="submit" name="_method" value="POST">SALVAR</button>    
 <button type="submit" name="_method" value="DELETE">DELETAR</button><br>  
[/code]

E o controller redirecionando para o proprio form

Ao invés de mandar para o método erro() envie para o métod responsável por chamar o teu formulário.

Blza Garcia, valeu mais uma vez. Eu cheguei a fazer isso, e funcionou. Só que tem um detalhe.

Meu form na verdade é minha list também.

Então, quando eu fiz dessa forma, depois que eu validei, ao invés de eu redirecionar para a minha página do formulário, eu estou redirecionando para o meu método list(), e dessa forma não está funcionando.

Quando eu fiz dessa forma, ele me retornou o formulário, com a mensagem de erro, mas com minha lista em branco.

Bruno, às ordem, hehehe.

Não sei se entendi bem sua dúvida… mas o list e form são na mesma tela? Você pode postar seu controller?

Abraços

validator.onErrorUse(logic()).forwardTo(MeuController.class).list();

Vamo lá!!!

package bsr.vraptor.controller;

import java.util.List;

import br.com.caelum.vraptor.Delete;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.Validator;
import br.com.caelum.vraptor.validator.ValidationMessage;
import br.com.caelum.vraptor.view.Results;
import bsr.hibernate.dao.ProjetoDao;
import bsr.hibernate.tabela.Projeto;


@Resource
public class ProjetoController {

	private Result result;
	private ProjetoDao dao = new ProjetoDao();
	private Validator validator;
	
	public ProjetoController(Result result, Validator validator){
		this.result = result;
		this.validator = validator;
	}
	
	@Path("/projetos")  
	@Post
	public void insert(Projeto projeto){
		//Validação
		if(projeto.getNome().equals("")){
			validator.add(new ValidationMessage("Nome em Branco","nomeEmBranco"));
		}
		//validator.onErrorUse(Results.page()).of(getClass()).erro();
		validator.onErrorUse(Results.logic()).redirectTo(getClass()).list();
		
		//Inserção e redirecionamento de página
		dao.insert(projeto);
		result.use(Results.logic()).redirectTo(getClass()).list();
	}
	
	@Path("/projetos")  
	@Delete 
	public void delete(Projeto projeto){
		dao.delete(projeto);
		result.use(Results.logic()).redirectTo(getClass()).list();
	}
	
	public List<Projeto> list(){
		return new ProjetoDao().list();
	}
	
}

E o JSP, que contém meu formulário num quadrante e minha lista em outro. Fora que cada linha minha possui a mesma estrutura do formulário.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<HTML>
<BODY>


<FONT FACE="Verdana" COLOR="DarkBlue">

	<c:import url="/WEB-INF/cabecalho.html"/>
	<FONT SIZE=-4><h1 ALIGN=CENTER>PROJETOS</h1></FONT>
	
	

	<table border="1" width="100%" cellpadding="10">
	<tr>
	
		<td width="30%" valign="top">
		<form action="<c:url value="/projetos"/>"/>
		
			<c:forEach var="error" items="${errors}">  
				${error.message}
			</c:forEach>
			
			Projeto: <br><input name="projeto.nome"/><br>
			<button type="submit" name="_method" value="POST">SALVAR</button>  
			<button type="submit" name="_method" value="DELETE">DELETAR</button><br>
		</form>
		</td>
		
		<td width="70%" valign="top">
		<table border="0" align="center" width="100%" cellspacing="0">
		<tr><th>Nome do Projeto</th></tr>
		<c:forEach var="projeto" items="${projetoList}">
			<tr>
				<td width="80%" valign="top">
				<form action="<c:url value="/projetos"/>"/>
					<input name="projeto.nome" value="${projeto.nome}" size="120" style="background-color: orange; color: darkblue; font-weight: bold"/>
				</td>
				<td width="10%" valign="top">
					<button type="submit" name="_method" value="POST" style="background-color: darkblue; color: white; font-weight: bold">SALVAR</button> 
				</td>
				<td width="10%" valign="top">
					<button type="submit" name="_method" value="DELETE" style="background-color: darkblue; color: white; font-weight: bold">DELETAR</button>  
				</td>
				</form>
			</tr>
		</c:forEach>
		</table>
		</td>
	
	</tr>
	</table>
	
</FONT>
</BODY>
</HTML>

Então, o código:

<c:forEach var="error" items="${errors}">    
       ${error.message}  
</c:forEach>  

Não está escrevendo a mensagem.

Linha 37: validator.onErrorUse(Results.logic()).redirectTo(getClass()).list();

Os erros possuem escopo request, sendo assim se você fizer redirect perde os dados. Altere para forward.

Abraços

muda a validação para:

validator.onErrorUse(Results.logic()).forwardTo(getClass()).list();  

Pois é, eu fiz isso tbm, e até fiz de novo agora pra ter certeza, porém ele não lista

Bruno, só não entendi uma coisa: com o forward não mostra as mensagens ou tua lista de projetos?

troca o

 public List<Projeto> list(){  
         return new ProjetoDao().list();  
  }

por

 public void list(){  
         result.include("projetoList", new ProjetoDao().list());  
  }

que vai funcionar…

é um bug no vraptor… ele não está adicionando o retorno do método nem no escopo flash (que faria funcionar o redirect) nem no forward… =(

faz essa alteração e usa o redirectTo que deve funcionar (na verdade era pra ele já estar funcionando antes… vou ver aqui)
senão troca pra forwardTo

vou cadastrar essa issue no github

obrigado

Valeu Garcia e Lucas

Ele mostrava as mensagens mas não listava meus projetos. Mas agora que o usei o:

result.include("projetoList", new ProjetoDao().list());    

ele está funcionando. Deu um erro no console que eu não consegui entender, mas funcionou.

15:18:17,046  WARN JstlLocalization:50 - couldn't find message bundle, creating an empty one
java.util.MissingResourceException: Can't find bundle for base name messages, locale pt_BR
	at java.util.ResourceBundle.throwMissingResourceException(Unknown Source)
	at java.util.ResourceBundle.getBundleImpl(Unknown Source)
	at java.util.ResourceBundle.getBundle(Unknown Source)
	at br.com.caelum.vraptor.core.JstlLocalization.getBundle(JstlLocalization.java:48)
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:81)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:54)
	at br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:51)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.multipart.MultipartInterceptor.intercept(MultipartInterceptor.java:58)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:58)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.core.URLParameterExtractorInterceptor.intercept(URLParameterExtractorInterceptor.java:45)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:70)
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:57)
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:71)
	at br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:71)
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:99)
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:37)
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:97)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
	at java.lang.Thread.run(Unknown Source)

Valeu mesmo!!!

esse erro que deu é pq você não deve ter um messages.properties no classpath, daí ele cria um vazio…

você deve criar esse arquivo se vc quiser internacionalizar sua aplicação, ou simplesmente externalizar as strings dos jsps

Olha eu de novo!!!

A boa prática pede para eu validar no cliente primeiro para não enviar requisições desnecessárias para o servidor. Bom, usei javascript então.

<script language="javascript">
//validação de formulário
function validaForm(){
 	if (document.getElementById("projeto_nome").value == ""){
 		alert("Campo NOME em branco");
 		document.projeto.nome.focus();
        return false;
	}
	return true;
}  
</script>
<BODY onload="validaForm">


<FONT FACE="Verdana" COLOR="DarkBlue">

	<c:import url="/WEB-INF/cabecalho.html"/>
	<FONT SIZE=-4><h1 ALIGN=CENTER>PROJETOS</h1></FONT>
	
	

	<table border="1" width="100%" cellpadding="10">
	<tr>
	
		<td width="30%" valign="top">
		<form onSubmit="return validaForm();"
			  action="<c:url value="/projetos"/>">
			Projeto: <br><input name="projeto.nome" id="projeto_nome"/><br>
			<small><FONT COLOR="Red"><c:out value='${errors[0].message}'/></FONT></small>
			<button type="submit" name="_method" value="POST">SALVAR</button>  
			<button type="submit" name="_method" value="DELETE">DELETAR</button><br>
		</form>
		</td>
...

O problema é o seguinte: eu valido no cliente mas não to conseguindo escapar da validação no servidor. Eu gostaria de executar o “return validaForm()” e não ir para a url do meu action, e nem para url nenhuma, ou seja, não fazer nada, continuar no meu formulário.

Como fazer isso?
Agradeço mais uma vez!!!
Obrigado!

Dá uma olhada nesse plugin do jquery:

http://docs.jquery.com/Plugins/Validation

Olá, o bug anterior, do retorno do método, foi corrigido… quando sair a nova versão, você vai poder dar forwards para métodos que retornam valores sem problemas

e se preferir, baixe o codigo fonte e basta execuar o ant para obter um SNAPSHOT da versao mais recente!

Pô, bem bacana esse JQuery hein. Bem simples e melhor do que o javascript que eu tinha feito :slight_smile:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<HTML>

<HEAD>

<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="http://dev.jquery.com/view/trunk/plugins/validate/jquery.validate.js"></script>
<script>
	$(document).ready(function(){
    	$("#projetoForm").validate({
        	rules:{
    			"projeto.nome":{required:true}
        	},
        	messages:{
        		"projeto.nome":{required:"O campo NOME está em branco"}
        	}
    	});
  	});
</script>

<BODY>
<FONT FACE="Verdana" COLOR="DarkBlue">

	<c:import url="/WEB-INF/cabecalho.html"/>
	<FONT SIZE=-4><h1 ALIGN=CENTER>PROJETOS</h1></FONT>
	
	<table border="1" width="100%" cellpadding="10">
	<tr>
	
		<td width="30%" valign="top">
		<form id="projetoForm"
			  action="<c:url value="/projetos"/>">
			
			Projeto: <br><input name="projeto.nome" id="projeto_nome"/><br>
			<small><FONT COLOR="Red"><c:out value='${errors[0].message}'/></FONT></small>
			<button type="submit" name="_method" value="POST">SALVAR</button>  
			<button type="submit" name="_method" value="DELETE">DELETAR</button><br>
			
		</form>
		</td>
...

Valeu!!!

Eh galera, não achei que negócio de validação / internacionalização seriam o maior dos meus problemas.

Achei esse link que meu deu um norte na tentativa de solução do meu problema:
http://celodemelo.wordpress.com/2007/11/05/internacionalizacao-de-mensagens-no-javascript/

Consiste numa Servlet que lê as mensagens de acordo com meu locale e retorna na forma de um JSON. No entanto, ao invés de Servlet, fiz uma Controller para internacionalizar as mensagens na minha Jquery:

[code]
@Resource
public class I18nController {

private Result result;
private HttpServletRequest request;
private HttpServletResponse response;


public I18nController(Result result, HttpServletRequest request,HttpServletResponse response)
throws Exception{
	this.result = result;
	this.request = request;
	this.response = response;
	Locale locale = this.request.getLocale();
	PrintWriter out = this.response.getWriter();
	out.print(getJsonMensagens(locale));
	out.flush();
	out.close();
}

private String getJsonMensagens(Locale locale){
	ResourceBundle mensagens = ResourceBundle.getBundle("messages",locale);
	StringBuilder json = new StringBuilder();
	json.append("[");
	Set<String> keys = mensagens.keySet();
	for(String key:keys){
		json.append("{key:'" + key + "', value:'" + mensagens.getString(key)+ "'},");
	}
	int i = json.lastIndexOf(",");
	json.replace(i,i+1,"");
	json.append("]");
	
	return json.toString();
}

}[/code]

Jquery:

<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="http://dev.jquery.com/view/trunk/plugins/validate/jquery.validate.js"></script>

<script>

	var bundle = new ResourceBundle();

	$.get("i18n",function(data){
		bundle.initialize(data);
	});

	function ResourceBundle(){
		this.messages = [];
	};

	ResourceBundle.prototype.initialize = function(json){
		var objs = eval(json);
		for(var i = 0; i < objs.length;i++){
			var obj = objs[i];
			this.messages[obj.key] = obj.value;
		}
	};

	ResourceBundle.prototype.get = function(pKey){
		return this.messages[pKey];
	};

        //VALIDAÇÃO QUE ESTÁ FUNCIONANDO
	$(document).ready(
		function(){
    		$("#projetoForm").validate({
        		rules:{
    				"projeto.nome":{required:true}
        		},
        		messages:{
        			"projeto.nome":{required:bundle.get("projeto.valida.nome")}
        		}
    		});
  		}
  	);

	
</script>

Mas parece que meu código Jquery não está requisitando o controller. Não nenhum erro, mas a mensagem de validação que retorna é a mensagem padrão, e não a internacionalizada.

Na chamada do controller:

coloque “i18n” pela convenção do Vraptor. Será que é isso que está errado?

Agradeço desde já a ajuda que tenho recebido.
Obrigado!!!

Não entendi muito bem. O código do controller nem sequer chega a ser executado?

Creio que você não possa fazer tudo no construtor. Lembre-se do Java básico que nem sempre você tem todos os recursos da classe antes do construtor ser completamente finalizado. Tente algo como:

[code]@Resource
public class I18nController {

private Result result;
private final PrintWriter writer;
private final Locale locale;

public I18nController(Result result, HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    this.result = result;
    locale = request.getLocale();
    writer = response.getWriter();
    response.setContentType("application/json"); // jquery exige que o content-type seja json
}

@Path("/i18n")
public void getJsonMensagens() {
    ResourceBundle mensagens = ResourceBundle.getBundle("messages", locale);
    StringBuilder json = new StringBuilder();
    json.append("[");
    Set<String> keys = mensagens.keySet();
    for (String key : keys) {
        json.append("{key:'" + key + "', value:'" + mensagens.getString(key) + "'},");
    }
    int i = json.lastIndexOf(",");
    json.replace(i, i + 1, "");
    json.append("]");

    writer.print(json.toString());

    result.use(Results.nothing()); // vai para lugar algum
}

}[/code]

A propósito, fiz um componente para tratar json, você tem interesse?