Exception handler no vraptor3

33 respostas
G

Boa noite pessoal.

Eu estava trabalhando em um exception handler para minha aplicação usando vraptor. Porém um pequeno problema pessoal me deixou um pouco offline nesses ultimos dias, e acabei tendo pouquíssimo tempo para isso.

Com esse pouco tempo o que eu fiz foi apenas criar um interceptor com um try and catch. Caso cair no catch ele verifica o tipo da exception e imprime na tela as mensagens. Pouca coisa, não?

Meu objetivo é fazer algo semelhante com o que eu tinha no struts 1x, onde eu verificava o tipo de exception e a partir dela eu poderia fazer a decisão de para onde ir. Normalmente eu ia para a tela anterior com os dados do form para erros de negócio ou erros que eu possa recuperar. Caso ocorrer erros irrecuperáveis (banco fora do ar, etc) eu apenas exibia uma tela com as mensagens.

Então preciso de uma opinião de vocês sobre como seria a melhor forma de indicar ao exception handlers para onde ele deve ir em caso de exception (de inicio pensei em uma annotation @OnError ou um results.onException).

Abraços

33 Respostas

Lucas_Cavalcanti

pelo que eu entendi, as exceções não recuperáveis vão sempre pro mesmo lugar, certo?

quanto às exceções recuperáveis, não é possível prevení-las com validações?

tipo, um NullPointer é facilmente evitado com uma verificação… isso meio que acontece com
todos os outros erros “recuperáveis”…

onde as exceções seriam lançadas? na sua lógica? no seu modelo?

[]'s

G
pelo que eu entendi, as exceções não recuperáveis vão sempre pro mesmo lugar, certo?

Sim, para uma tela que apenas imprime a mensagem de erro.

quanto às exceções recuperáveis, não é possível prevení-las com validações?

Na verdade no meu controller tenho as validaçoes de campos obrigatórios apenas, depois chamo os EJBs e redireciono para view. Esses erros que quero tratar com um exception-handler seriam os erros de negócio mesmo.

Minha idéia é não ficar fazendo try and catch a cada vez que chamo os EJBs para tratar esses erros. Penso em algo automatizado.

Atualmente eu tenho isso aqui:

public void delete(final Long id) {
	// validate required fields
	validator.checking(new Validations() {
		{
			that(id == null, "erro", "blah");
		}
	});

	validator.onErrorUse(Results.page()).of(getClass()).list();

	// call the ejb method
	try {
		coreService.deleteDegree(id);
	} catch (Exception e) {
		validator.add(new ValidationMessage(e.getMessage(), "error"));
	}

	validator.onErrorUse(Results.page()).of(getClass()).list();

	// forward if operation success
	result.use(Results.logic()).redirectTo(getClass()).list();
}

Acho esse código do try and catch para tratar os erros lançados pelo EJB um pouco exaustivas. Exatamente para esses códigos que eu pensei no exception handler.

public void delete(final Long id) {
	// validate required fields
	validator.checking(new Validations() {
		{
			that(id == null, "erro", "blah");
		}
	});

	validator.onErrorUse(Results.page()).of(getClass()).list();

	// call the ejb method - if an error occurs the exception handler can be catch
	coreService.deleteDegree(id);

	// forward if operation success
	result.use(Results.logic()).redirectTo(getClass()).list();
}
Lucas_Cavalcanti

bom… o que você vai ter que mudar pra isso é o ExecuteMethodInterceptor…

dentro dele tem um try…catch…

você vai ter que adicionar essa sua lógica de erros dentro dele… (na verdade ele coloca a exceção que deu dentro de uma
InterceptionException, ou algo do tipo, vc pode capturá-la em outro lugar também…)

daí vc pode fazer um interceptor que o accepts dele são os métodos anotados com @OnError, e o
intercepts dele faz um try…catch e em caso de erro vai para a página especificada… só que daí não dá pra fazer uma
interface fluente… no máximo a classe e o nome do método pra onde vc quer ir…

sergiolopes

Para exceptions nao recuperaveis (sqlexceptio etc) use o proprio <error-page> no web.xml

G

lucascs:
daí vc pode fazer um interceptor que o accepts dele são os métodos anotados com @OnError, e o
intercepts dele faz um try…catch e em caso de erro vai para a página especificada… só que daí não dá pra fazer uma
interface fluente… no máximo a classe e o nome do método pra onde vc quer ir…

Lucas, até aí foi o que eu pensei. Para o handler eu não vou conseguir fazer uma fluent interface. Por isso pensei no @OnError. Porém o máximo que consigo seria colocar uma String para indicar para onde ir quando der erro, já que annotations somente aceita constantes.

Vou fazer mais uns testes aqui com tua sugestão. Obrigado.

:thumbup:

Lucas_Cavalcanti

então… você pode criar uma classe

@Component
public class MyResult {
      public class MyResult(Result result) {
            this.delegate = result;
      }

      //delega todos os métodos que vc usa
      public <T> T onErrorUse(Class<T> controller) {
            return proxifier.proxify(controller, new MethodInvocation() {
                public void ....(Object proxy, Method method,  Object[] args...) {
                         MyResult.this.method = method;
                         MyResult.this.args = args;
                         return null;
                 }
           }
      }

      public Method getErrorMethod() {
            return this.method;
      }
      public Object[] getErrorArgs() {
            return this.args;
      }
}

vc tem que receber nas suas lógicas que vão usar isso esse MyResult ao invés do Result, e chamar no começo da lógica:

myResult.onErrorGoto(BlahController.class).blahMethod();

daí no seu interceptor é só usar o myResult.getErrorMethod() e redirecionar pra ele em caso de erro:

Object instance = myResult.use(logic()).redirectTo(errorMethod.getDeclaringClass());
    errorMethod.invoke(instance, errorArgs);

isso deve funcionar, corrigindo alguns erros de compilação que eu deixei :wink:

que acha?

G

Lucas, essa classe MyResult estende ou implementa alguém?

Lucas_Cavalcanti

Não precisa… mas se você quiser pode extender DefaultResult, ou implementar Result, e receber um DefaultResult no construtor…

mas onde você precisar usar esse onError, tem que receber explicitamente um MyResult

G

Lucas, funcionou tudo bem mesmo. Adicionei um if para evitar NullPointerException caso eu não especificar o onError. Porém como faço agora para reinjetar os dados que o usuário digitou na tela anterior, e que estavam injetados no meu bean?

Ahh, alterei o myResult.use(logic()).redirectTo(errorMethod.getDeclaringClass()); para FORWARD.

Abraços

Lucas_Cavalcanti

isso de reinjetar os dados digitados já está implementado, no repositório…

mas só funciona diretamente pra erros de validação, você vai ter que colocar no seu interceptor de erro:

outjector.outjectRequestMap();

e receber um br.com.caelum.vraptor.validator.Outjector no construtor.

baixa o source mais novo e gera o jar, que isso vai funcionar

[]'s

G

Lucas, estou usando a última versão do repositório.

Fiz a alteração e funcionou perfeitamente. Vou fazer mais uns ajustes e otimizações e logo publico aqui o componente.

Abraços

G

Lucas, meu problema agora é que as mensagens de erro não estão sendo enviadas para a tela:

Method errorMethod = result.getErrorMethod();
Object[] args = result.getErrorArgs();
Object target = result.use(Results.logic()).forwardTo(errorMethod.getDeclaringClass());

validator.add(new ValidationMessage(ExceptionUtils.getRootCauseMessage(e), "error"));
outjector.outjectRequestMap();
<c:if test="${not empty errors}">
    <div class="errors">
        <ul>
            <c:forEach items="${errors}" var="e">
                <li>${e.category}: ${e.message}</li>
            </c:forEach>
        </ul>
    </div>

    <br clear="all"/>
</c:if>
Lucas_Cavalcanti

desculpe, esqueci de falar… não dá pra mexer muito bem com o validator de dentro de um interceptor…
(a validação só acontece de fato na chamada validation.onErrorUse(…), que joga exceção se tiver erro… isso não funciona num interceptor)

você pode fazer o seguinte:

result.include("errors", Arrays.asList(new ValidationMessage(...)));

funciona do mesmo jeito, e como você não vai chamar o stack.next, não tem problema…

outra coisa, coloque o outjector.outjectRequestMap() antes da chamada result.use(…)

[]'s

G

Lucas, pois é, depois que postei a mensagem pensei: me parece errado jogar mensagens de exceptions dentro de um validator. Então fiz de forma muito parecido com o que você fez.

Abraços, e obrigado.

G

Lucas, tive um outro problema agora. Quando salvo um objeto e o EJB lança uma exception de negócio, estou com problemas ao buscar os dados na tela.

Nesse caso no controller roda tudo bem. Quando dá um erro no método store, o método edit é chamado. Porém nesse caso como eu estava na inserção ele entra no método edit passando null como parametro. Sendo assim o objeto Year não é carregado (está certo esse procedimento). Então no JSP tenho o form com alguns campos de input. Quando ele encontra inputs que não são String dá esse erro. Notei que se eu removo os campos id (long), startDate (Date) tudo funciona. Será que há algo no outjector.outjectRequestMap()?

Aqui como ficou meu CustomExecuteMethodInterceptor

final List<Message> messages = new LinkedList<Message>();
messages.add(new ValidationMessage(ExceptionUtils.getRootCauseMessage(e), "error"));
result.include("errors", messages);

outjector.outjectRequestMap();

Method errorMethod = result.getErrorMethod();
Object[] args = result.getErrorArgs();
Object target = result.use(Results.logic()).forwardTo(errorMethod.getDeclaringClass());

try {
	errorMethod.invoke(target, args);
} catch (Exception e0) {
	throw new InterceptionException(e0);
}
public void edit(Integer id) {
    if (id != null) {
        result.include("year", yearService.findByPK(id));
    }
}

public void store(YearDTO year) {
    result.onErrorUse(getClass()).edit(year == null ? null : year.getId());

    yearService.store(year);
    result.use(Results.logic()).redirectTo(getClass()).list();
}

O JSP de edição:

<w:text name="year.id" class="number" size="4" maxlength="4" />
<w:date name="year.startDate" pattern="dd/MM/yyyy" size="10" class="date" />
Caused by: java.lang.IllegalArgumentException: Cannot format given Object as a Number
	at java.text.DecimalFormat.format(DecimalFormat.java:487)
	at java.text.Format.format(Format.java:140)
	at org.apache.taglibs.standard.tag.common.fmt.FormatNumberSupport.doEndTag(FormatNumberSupport.java:230)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx._jspx_meth_fmt_formatNumber_0(year_edit_jspx.java from :209)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx._jspx_meth_fmt_param_0(year_edit_jspx.java from :181)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx._jspx_meth_fmt_message_0(year_edit_jspx.java from :148)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx.access$000(year_edit_jspx.java from :7)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx$year_edit_jspxHelper.invoke0(year_edit_jspx.java from :839)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx$year_edit_jspxHelper.invoke(year_edit_jspx.java from :858)
	at org.apache.jsp.tag.web.default_tag.doTag(default_tag.java from :180)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx._jspx_meth_tags_default_0(year_edit_jspx.java from :127)
	at org.apache.jsp.WEB_002dINF.jspx.management.year_edit_jspx._jspService(year_edit_jspx.java from :102)
G

Fiz agora mais um debug, e pelo que noto está sendo colocado o objeto outjectMap no request, ao invés do tipo correto.

Caused by: java.lang.IllegalArgumentException: Cannot convert true of type class br.com.caelum.vraptor.view.RequestOutjectMap to class java.lang.Boolean at com.sun.el.lang.ELSupport.coerceToBoolean(ELSupport.java:180) at com.sun.el.lang.ELSupport.coerceToType(ELSupport.java:360) at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:209) ... 84 more

Se eu coloco os campos na tela como String funciona tudo bem. Quando coloco nas taglibs que precisam fazer evaluate para Date, Boolean, Double dá esse erro.

G

Pelo que notei o vraptor usa como atributo do request um objeto RequestOutjectMap. Não consegui ainda entender por qual razão que as telas funcionam bem, porém apenas quando há uma exception capturada pelo exception handler o RequestOutjectMap resolve a encrencar com o evaluate as ELs do glassfish.

O mais estranho é que mesmo que eu tenha os parametros corretos esse objeto está sempre retornando null em RequestOutjectMap.getAsString().

G

Galera, muito obrigado pelo help. Consegui resolver o problema. Não sei se sou eu que não soube usar corretamente o RequestOutjectMap ou se há realmente um problema no componente. Notei que ele existe desde a versão 3.0.2 (estou usando o snapshot dessa versão). Mas para contornar ao invés de usar ele estou reinjetando os parametros manualmente no meu exception handler. Desculpem o código muito feio, mas foi o que consegui fazer na correria. Logo mais a noite acho que consigo tempo de mexer com calma nisso.

Enumeration<String> params = request.getParameterNames();
while (params.hasMoreElements()) {
	String name = params.nextElement();
	Object value = request.getParameter(name);

	if (name.contains(".")) {
		final int iname = name.indexOf(".");
		String oname = name.substring(0, iname); // the attribute name
		Map<String, Object> map = (Map<String, Object>) request.getAttribute(oname);
		if (map == null) {
			map = Maps.newLinkedHashMap();
		}

		map.put(name.substring(iname + 1), value); // the property

		request.setAttribute(oname, map); // set again for cluster propagation
	} else {
		request.setAttribute(name, value); // this is a single property
	}
}
Lucas_Cavalcanti

Olá garcia,

acabei deixando passar esse tópico do guj, desculpe =(

esse código que você fez funciona no seu caso?

se funcionar a gente pode substituir a implementação do nosso RequestOutejctMap (que é talvez um pouco gambiarra demais)

[]'s

Lucas_Cavalcanti

eu reabri a issue pra gente dar uma olhada nela

G

lucascs, por enquanto está funcionando de forma quase satisfatória., pois detectei um problema nele.

Se você notar bem eu “seto” os dados nesse map como String mesmo, já que vem do HttpServletRequest.getParameter. Enfim, o EL funciona direitinho com todos tipos, seja ele um boolean, double… porém apenas com Date ele não funciona, pois eu adiciono no Map como String. Isso é uma coisa que preciso rever.

java.lang.IllegalArgumentException: Cannot convert 11/11/1111 of type class java.lang.String to class java.util.Date at com.sun.el.lang.ELSupport.coerceToType(ELSupport.java:381) at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:209) at org.apache.jasper.runtime.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:137) at org.codehaus.waffle.taglib.form.DateTag.evaluateDate(DateTag.java:120) at org.codehaus.waffle.taglib.form.DateTag.start(DateTag.java:105) at org.codehaus.waffle.taglib.form.FormElement.doStartTag(FormElement.java:73) at org.codehaus.waffle.taglib.form.DateTag.doStartTag(DateTag.java:90)

Um potencial erro, que na verdade não sei se vai ou não acontecer, é quando eu já possuo o bean como atributo de request. Note que eu trato tudo como se fosse um Map com as propriedades já que o JSTL trabalha bem assim. Nesse caso se eu tiver já um atributo com esse nome e for do tipo do objeto eu vou ganhar um ClassCastException. Vou fazer mais uns testes nisso.

Abraços

Lucas_Cavalcanti

é… a gente faz quase isso… mas passa um RequestOutjectMap com o toString sobrescrito… mais feio :stuck_out_tongue: sua implementação parece bem melhor =)

G

Lucas, estive analisando que esse objeto só é usado para as validações e no meu caso para o exception-handler. O problema que eu tenho seria do parse de valores como date e afins, que as EL não conseguem trabalhar.

No meu caso eu faço um forward para um outro logic, ou seja, penso que eu poderia reinjetar os parametros da requisição de novo no método via ONGL, como é feito quando eu acesso um URL qualquer. Isso resolveria nosso problema, pois aí ele chamaria os converters que estão registrados e tudo ficaria bem.

Porém teriamos um problema caso alguém for fazer um redirect direto para um JSP e não para um logic. Aí nesse caso essa solução com o Map faz mais sentido.

O que você acha?

Lucas_Cavalcanti

Então… mesmo que seja um forward pra logica, as lógicas que vão pra formulários de adição geralmente não recebem parâmetro nenhum, ou pelo menos não o objeto que estamos tentando criar com o formulário…

infelizmente o OGNL não vai ajudar, acho que vai ter que ser o mapa ou algo do tipo mesmo =(

Felipe_T_Farias

Olá a todos !

Estive tendo o mesmo problema, e depois de muito penar, finalmente entendi qual o problema no meu caso. No meu JSP eu tenho:

<form action="<c:url value="/notasFiscaisEntrada/gravar"/>" method="post">
....
<label for="dataEntrada">Data Entrada:</label>
            <input type="text" id="dataEntrada" name="notaFiscalEntrada.dataEntrada"
                   value="<fmt:formatDate pattern="dd/MM/yyyy" value="${notaFiscalEntrada.dataEntrada}" />"/><br />
....
</form>

Quando executo pela primeira vez não há problema, já que notaFiscalEntrada.dataEntrada é do tipo java.util.Date.

Porem quando retorno a esta página após um erro, ela quebra com o erro

Cannot convert 01/09/2010 of type class java.lang.String to class java.util.Date

Bem, o que esta acontecendo ? No vRaptor (3.1.0) temos o DefaultValidator:

public class DefaultValidator implements Validator {
...
    public <T extends View> T onErrorUse(Class<T> view) {
    	if (!hasErrors()) {
    		return new MockResult().use(view); //ignore anything, no errors occurred
    	}
    	result.include("errors", errors);
    	outjector.outjectRequestMap();
    	return viewsFactory.instanceFor(view, errors);
    }
...
}

Na chamada outjector.outjectRequestMap() todos os campos são transformador em String. E a tag necessita de um um value do tipo java.util.Date.

Como resolver ? No meu caso, criei uma taglib de formatação que pode aceitar tanto String quanto Date

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
  <tlib-version>1.0</tlib-version>
  <short-name>xxx</short-name>
  <uri>/WEB-INF/tlds/xxxTagLibrary</uri>
  <tag>
    <name>formatDate</name>
    <tag-class>com.xxx.ui.util.tag.FormatDateTagHandler</tag-class>
    <body-content>empty</body-content>
    <attribute>
      <name>pattern</name>
      <rtexprvalue>true</rtexprvalue>
      <type>java.lang.String</type>
    </attribute>
    <attribute>
      <name>value</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <type>Object</type>
    </attribute>
  </tag>
</taglib>

e a classe:

public class FormatDateTagHandler extends SimpleTagSupport {

    private String pattern;
    private Object value;

    @Override
    public void doTag() throws JspException {
        JspWriter out = getJspContext().getOut();

        try {
            JspFragment f = getJspBody();
            if (f != null) {
                f.invoke(out);
            }

            if (value == null) {
                out.print("");
            } else if (value instanceof Date) {
                out.print(getDateFormat().format((Date) value));
            } else {
               // aqui tratamos na segunda chamada depois de um erro, quando a campo esta como String
                out.print(value);
            }
        } catch (java.io.IOException ex) {
            throw new JspException("Error in FormatDateTagHandler tag", ex);
        }

    }

    private DateFormat getDateFormat() {
        if (pattern == null) {
            return new SimpleDateFormat("dd/MM/yyyy");
        } else {
            return new SimpleDateFormat(pattern);
        }
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

o JSP ficou quase o mesmo:

<%@ taglib prefix="xxx" uri="/WEB-INF/tlds/xxxTagLibrary" %>
... 
<label for="dataEntrada">Data Entrada:</label>
<input type="text" id="dataEntrada" name="notaFiscalEntrada.dataEntrada"
    value="<xxx:formatDate pattern="dd/MM/yyyy" value="${notaFiscalEntrada.dataEntrada}" />"/><br />

e pronto, não tive mais problemas. Ainda precisa de alguns ajustes finos, mas a idéia geral é esta.

Espero que ajude !

Felipe T. Farias

D

garcia-jj:
… lança uma exception de negócio, estou com problemas ao buscar os dados na tela.

Nesse caso no controller roda tudo bem. …

(Seguindo o conselho do Garcia…)
VRaptor forks, contribuidores e entusiastas,
Resumo da Opera: Qual é a abordagem + profissional para propagar tanto as Mensagens de [color=red]Erro de Negócio, [/color]como as Mensagens de [color=darkblue]Sucesso[/color] até a Visão :?: ?!
Obs.: qq contribuição é de suma importância! :!:

G

Derlon, a abordagem mais profissional eu não sei bem qual seria, mas vou te explicar como eu uso. Obviamente vou resumir aqui porque já falei muito sobre isso, e se você der uma olhada no post http://guj.com.br/posts/list/30/136307.java#789504 vai entender melhor como eu uso. Não sei se é o mais correto, porém foi o mais prático que achei, já que todos meus projetos são muito grandes (em quantidade de classes mesmo).

Basicamente tenho um exception handler que quando der um erro ele volta para alguma tela que eu disser para ele ir. Sempre que der um erro, ele tenta pegar a mensagem da root exception e retorna para tal dela que eu especifiquei exibindo a mensagem. Basicamente algo como abaixo:

public void list(Paging paging) {
        result.onExceptionUse(UserSpaceController.class).dashboard();

        result.include("branchList", branchService.find(paging));
    }

    public void edit(Long id) {
        result.onExceptionUse(getClass()).list(null);

        if (id != null) {
            result.include("branch", branchService.findById(id));
        }
    }

Nesse caso se você entrar no método list e der algum erro qualquer e que você não trate via try and catch o exception handler irá atuar redirecionando para a dashboard. E caso eu entrar no edit e der um erro, o exception handler irá fazer um forward para o list.

Enfim, essa é a forma que eu uso. Como o vraptor ainda não tem esse exception handler você pode fazer o processo mais manual ou então usar esse cara na tua aplicação. Caso você queira fazer manual, usando try and catch, você pode fazer algo conforme meu código abaixo. No frigir dos ovos o resultado é o mesmo, a vantagem do exception handler é você não precisar fazer try and catch, validator.add e validator.onErrorUse a cada método do controller.

try {
            result.include("branchList", branchService.find(paging));
        } catch (Exception e) {
            validator.add(new ValidationMessage(e.getMessage(), "error"));
            validator.onErrorUse(Results.logic()).forwardTo(UserSpaceController.class).dashboard();
        }

Se você não entendeu algo, me avise.

Abraços

D

Paulo Silveira:
… Sobre essa parte de tratamento de excecoes, nao estou vendo ainda a melhor solucao. Acho melhor esperarmos, ja que da pra contonar isso atraves de interceptadores ou mesmo do mecanismo padrao do web.xml …
Bem, então, por enquanto…

garcia-jj:

Basicamente tenho um exception handler que quando der um erro ele volta para alguma tela que eu disser para ele ir. Sempre que der um erro, ele tenta pegar a mensagem da root exception e retorna para tal dela que eu especifiquei exibindo a mensagem…
O “try and catch o exception handler”, na verdade, não TÃO [color=red]Harded[/color]-Coded, mas a sua abordagem permite 1 redução muito boa de código!!! :thumbup: Só ficou 1 pouco místico p/ mim: como é q faz p/s MSGsErro chegarem na View??! :?:

garcia-jj:
... validator.add(new ValidationMessage(e.getMessage(), "error")); ...
só num achei muito bacana misturar ‘Falhas de Validação’ com Mensagen (de Erro) de Negócio (ou ainda de Infra).
Mas, em termos gerais (p/ Apps com 1 n° considerável de UserCases) ficou muito elegante sua abordágem!!8) Valews mesmo!

G

derlon:
garcia-jj:
... validator.add(new ValidationMessage(e.getMessage(), "error")); ...
só num achei muito bacana misturar ‘Falhas de Validação’ com Mensagen (de Erro) de Negócio (ou ainda de Infra).
Mas, em termos gerais (p/ Apps com 1 n° considerável de UserCases) ficou muito elegante sua abordágem!!8) Valews mesmo!

Pois é, o código que coloquei aqui era uma sugestão do inicio de tudo. Como estava com pouco tempo coloquei isso para mexer logo mais que a idéia evoluísse. Você tem uma idéia melhor? Usei o “validator.addMessage” porque o vraptor não oferece nenhum outro local para jogar mensagens automaticas para a tela sem usar o result.include. Mas acho que a partir dessa minha idéia inicial podemos evoluir, o que você acha?

Essa foi a principal motivação, já que o código se torna bem cansativo com tantos try and catch.

G

http://guj.com.br/posts/list/204039.java

D

garcia-jj:
… Você tem uma idéia melhor? Usei o “validator.addMessage” porque o vraptor não oferece nenhum outro local para jogar mensagens automaticas para a tela sem usar o result.include. Mas acho que a partir dessa minha idéia inicial podemos evoluir, o que você acha?..
Bem, na verdade acabei seguindo a dica do Lucas p/ usar o Padrão do V|Raptor ‘result.include(“message”, “…”);’. E, somado a isto, adotei a Convenção’result.include(“msgFalha”, “…”);’ p/ Erros de Negócio/Infra e ‘result.include(“msgSucesso”, “…”);’ obvamente p/ MSGs de Sucesso!
E até modifiquei o LogOn, q anteriormente tinha colocado a lógica de Autenticação bem dentro da Validação,: mudando p/ a minha famosa Classe ‘SessaoDoUsuario’. Inicialmente, cheguei pensar em incluir a mensagem de “‘Nome de Acesso’ e/ou ‘Senha’ Inválidos” via “msgAutentic”, mas dpois eu pensei: não, tenho q ser simples; então passei ela pela “msgFalha”, q já fazia parte da minha Convenção!! :mrgreen:

G

Semana passada passei pro Lucas aquela alteração que tinhamos sugerido do Bean Validator, e o Lucas já publicou no master no Vraptor. POR ENQUANTO as validações estão caindo no objeto Validator.add(Message) sendo que a chave é o nome do campo.

public void validate(Object object) { if (beanValidators == null || beanValidators.isEmpty()) { logger.warn("has no validators registered"); } else { for (BeanValidator validator : beanValidators) { addAll(validator.validate(object)); } } }

Em uma versão de testes minhas coloquei diferente. Ao invés de retornar para o mesmo saco das mensagens de validação eu retorno em outro objeto para a tela, com o nome beanmessages. Assim sabe o que eu posso fazer? Quando imprime a tela ele mostra a mensagem de erro ao lado do campo.

public void validate(Object object) { List<Message> beanMessages = new LinkedList<Message>(); if (beanValidators == null || beanValidators.isEmpty()) { logger.warn("has no validators registered"); } else { for (BeanValidator validator : beanValidators) { beanMessages.add(validator.validate(object)); } result.include("beanMessages", beanMessages); super.add(new ValidationMessgage("Ocorreram erros de validação.", "error")); } }

Enfim, é uma abordagem diferente. Acho que você está correto de separar todo, no caso mensagens de validação em um lado, erros do sistema em outro e mensagens de confirmação em outro.

Porém optei nesse meu teste separar as mensagens do beanValidator para poder pintar os campos com erro e exibir uma mensagem ao lado.

Lucas, como vocês estão fazendo nos projetos da Caelum com Vraptor para exibir mensagens, digamos, mais inteligentes para esses casos (confirmação, erros de infra, erros validação, etc)?

Lucas_Cavalcanti

garcia-jj:

Lucas, como vocês estão fazendo nos projetos da Caelum com Vraptor para exibir mensagens, digamos, mais inteligentes para esses casos (confirmação, erros de infra, erros validação, etc)?

Depende dos casos…
a gente geralmente não usa ejbs, e previne erros de banco de dados com validações… as validações a gente sempre mostra em cima da página, todas juntas num lugar só…
as validações que ficam do lado do campo geralmente são feitas com javascript, pra não chegar a fazer requisições “inválidas”

Criado 6 de novembro de 2009
Ultima resposta 23 de abr. de 2010
Respostas 33
Participantes 5