使用spring webflow,在前台页面中如何显示后端返回的错误信息

刚刚接触spring webflow,相关的资料并不是很多,并且大都是那种入门的 .xml文件的配置。

用到的CAS 最新的4.0版本用的就是web-flow流,前台页面使用的是表单提交。于是我就碰到了一个问题,问题描述如下:

第一次输入错误的用户名密码提示用户名密码错误,第二次输入了正确的用户名和密码仍然报错,校验不通过。

在使用cas之前的登录页面如果输入用户名和密码错误的话,会弹出alert提示框,这个是使用ajax请求实现的。而ajax请求的一般都是那种spring mvc的controller,并且可以异步根据返回值回掉。但是cas的源码里面使用的是表单提交,表单提交不是异步的,所以除非加iframe,否则没法回调函数来刷新页面。而加了iframe之后也存在许多的改动,后续的工作量也不可评估。(为什么一定要回调呢,因为cas的页面表单里不只有用户名和密码输入框,还有个讨厌的隐藏元素:loginticket 和 flowExecutionKey,cas处理每次请求都需要重新生成一个 loginticket票据)。

想过几种解决方案,刚开始都是想webflow和mvc整合,两种方式:

1. 将login-webflow.xml中的end-state标签中的externalRedirect修改成我的某个controller并在参数里面带上错误信息,controller返回一个对象给到前端(一般是code:-1;msg:"";date:****这样的json串的形式)。那么前端ajax调用login这个webflow的时候,就会重定向到我定义的这个controller,并且拿到这个controller的返回值进行回调。

<end-state id="redirectView" view="externalRedirect:http://user/res?code=-1&msg="error"" />

但是这样有一个问题,弹出alert框之后点击确定,alert框消失了,再重新输入正确的用户名和密码,仍然会报错,原因是loginticket验证失败。关于loginticket校验的,可以从cas的源码中看到:

AuthenticationViaFormAction:
public final Event submit(final RequestContext context, final Credential credential,
            final MessageContext messageContext) throws Exception {
        // Validate login ticket
        final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context);
        final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context);
        if (!authoritativeLoginTicket.equals(providedLoginTicket)) {
            logger.warn("Invalid login ticket {}", providedLoginTicket);
            messageContext.addMessage(new MessageBuilder().code("error.invalid.loginticket").build());
            return newEvent(ERROR);
        }
……

是去拿flow流中的lt和请求中的票据lt进行比较的。然而表单中的票据没有更新成最新的,我的webflow结束之后才返回的错误信息,那自然拿不到webflow里面生成的票据了。

先抛开这个问题不谈,说第二种整合mvc的方法:

在end-state标签中加入output,output中输出一个对像;

第三种:继承AbstractFlowHandler方法,

public class OcLoginFlowEndHandler extends AbstractFlowHandler {
    public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request,
            HttpServletResponse response) {
        if (outcome.getId().equals("redirectView")) {
            return "/user/loginRes?code=" + outcome.getOutput().get("bookingId");
        } else {
            return "/hotels/index";
        }
    }
}

然而这都没有解决我的问题。因为出了那个webflow容器拿到的lt总是不对的。

那怎么在login-webflow这个闭环中返回错误信息给前台页面呢?

查到有一种方式:http://docs.spring.io/spring-webflow/docs/2.3.x/reference/htmlsingle/#end-state-element

使用一个viewstate,来展示popup 对话框:

?<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">

不过这个加了这个viewstate之后也得重新定向到login的viewstate才行。

最后我采用的方案:

修改login的viewstate:

    <view-state id="viewLoginForm" view="/platform/login" model="credential">
        <binder>
            <binding property="username" />
            <binding property="password" />
        </binder>
        <on-entry>
            <set name="requestScope.messages" value="messageContext.allMessages" />
            <set name="viewScope.commandName" value="‘credential‘" />
        </on-entry>
        <transition on="submit"  bind="true" validate="true" to="realSubmit">
            <evaluate
                expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credential)" />
        </transition>
    </view-state>

添加绿色的那一行,这个view在render到velocity页面的时候,直接把requestScope里的messages给放进去,前端控制,如果messages为空,就不展示,如果不为空,就展示出来。

<label style="float: left; display: inline-block; font-size: 12px; color: red; text-decoration: none; margin: 5px 0;">
                                    #foreach(${message} in ${messages} )
                                    #if(${message.text} == "Invalid credentials.")
                                        用户名、密码或验证码错误!
                                    #end
                                    #end
                                    </label>

done!

那么看下效果:

再输入正确的用户名和密码就不会报那个loginticket验证失败的错误并且能成功登录到系统中去了。

不足的是,其实很明显的看到页面刷新了一下。

时间: 2024-10-10 14:19:53

使用spring webflow,在前台页面中如何显示后端返回的错误信息的相关文章

获取前台页面中的值,将获取到的值传到后台,做简单的登录验证(不与数据库关联)

思路:利用jQuery的AJAX 前台代码: 1 <body> 2 <form id="form1" runat="server"> 3 <div> 4 <table> 5 <tr> 6 <td>用户名:</td> 7 <td> 8 <input type="text" id="name" name="Usernam

Spring MVC 向前台页面传值-ModelAndView

ModelAndView 该对象中包含了一个model属性和一个view属性 model:其实是一个ModelMap类型.其实ModelMap是一个LinkedHashMap的子类 view:包含了一些视图信息 当视图解释器解析ModelAndVIew是,其中model本生就是一个Map的实现类的子类.视图解析器将model中的每个元素都通过request.setAttribute(name, value);添加request请求域中.这样就可以在JSP页面中通过EL表达式来获取对应的值 1.向

获取表中字段最大值,并且保存在前台页面中

//获取Userid function getUserId(){ $.getJSON('<%=basePath %>user/getUserId.do', function(data){ alert(eval(data).userId); document.getElementById("userId").value=data.userId; }); } <input id="userId" name="userId"  val

怎样扩展EasyUI在页面中马上显示选中的本地图片

在编写前台页面的时候,有时须要将选中的图片夹杂着其它信息一起上传到服务端,在选着本地图片的时候,为了获得更好的效果,须要将该图片显示在页面上. 最初思路有两个.详细例如以下: 1.获取选中文件的二进制数据再传递给画板,画出图片来. 2.获取选中文件的传递给Img标签. 经过測试,得到例如以下结果: 1.Img标签的src无法指向本地路径的文件,这应该是基于安全考虑的结果吧. 2.通过File API 读取的文件二进制数据无法直接传递给Cancav画板画出选中图片,这也许也是基于安全考虑的结果.

如何扩展EasyUI在页面中立即显示选中的本地图片

在编写前台页面的时候,有时需要将选中的图片夹杂着其他信息一起上传到服务端,在选着本地图片的时候,为了获得更好的效果,需要将该图片显示在页面上. 最初思路有两个,具体如下: 1.获取选中文件的二进制数据再传递给画板,画出图片来. 2.获取选中文件的传递给Img标签. 经过测试,得到如下结果: 1.Img标签的src无法指向本地路径的文件,这应该是基于安全考虑的结果吧. 2.通过File API 读取的文件二进制数据无法直接传递给Cancav画板画出选中图片,这或许也是基于安全考虑的结果. 3.经过

同一标签内多个css规则在页面中如何显示?

这两天在学习css涉及到内联.外联.嵌入对页面的影响: 1.内联式-----将css代码直接写在html中.用 <style> 标记将样式定义为内部块对象.示例代码如下如下: <style type="text/css"> <!-- #user_nav{float:right;margin-right:20px;padding:4px; } --> </style> 内联 CSS 可以有效减少 HTTP 请求,提升页面性能,缓解服务器压力

Django + Mysql 中关于时间异常返回500错误的解决

问题描述: 最近在阿里云部署 Django(1.11.x) 时,在后台发布文章后,页面返回 500 异常. 刚开始的时候,遇到这个问题一脸懵逼,不知道该如何入手.后来把 settings.py 中 Debug=True ,然后运行网站,发现显示下面的错误信息: Database returned an invalid datetime value. Are time zone definitions for your database installed? 看到这个信息后就知道该怎么入手解决了,

前台页面中json和字符串相互转化

比如我有两个变量,我要将a转换成字符串,将b转换成JSON对象: var a={"name":"tom","sex":"男","age":"24"} var b='{"name":"Mike","sex":"女","age":"29"}'; 在Firefox,chrom

jquery 前台页面中时间戳转换成正常的日期格式

这个方法网上有好多,不过值得注意的是,你后台传过去的时间戳是不是精确到毫秒的问题 如果精确到毫秒就用下面的这个形式 new Date(parseInt(iteam.time)).toLocaleString().replace(/:\d{1,2}$/,' ') 如果没有精确到毫秒就用这种形式 new Date(parseInt(iteam.time)*1000).toLocaleString().replace(/:\d{1,2}$/,' ') 输出的结果是