0.action、screen、control的作用
action:用于接收请求参数,处理请求,并控制页面跳转。若跳转后的页面需要用到action中的参数,可以在action中将参数放到TemplateContext中。若是需要存储用户登录信息,可以将参数放到HttpSession当中。
screen:用于读取数据库中的信息,直接显示到页面,而没有用于显示页面的vm模板。
1.直接通过url访问Action
假设,要访问的Action类是user/AbcAction.java,方法是doSave(int id)
对应的url应该是:user/abcAction?action=user/abc_action&event_submit_do_save=anything&id=123
当有action参数时,其中的abcAction不重要,只要是abcAction类对应的目录即可,但要注意,提交到的方法一定要是doXxx,否则会报以下错误:
caused by com.alibaba.citrus.service.moduleloader.UnadaptableModuleException
Could not adapt object to module: type=action, name=AbcAction, class=class com.alibaba.webx.tutorial1.register.module.action.AbcAction
Could not find handler method for action event: null
action中对应的下划线表示法都会被mybatis改为java的驼峰表示法。有个mybatis设置是用于开关该表示法转换的。
2.通过form表单访问Action
在form表单中创建一个隐藏的input元素,name为action,value为目标Action.如,
<input type="hidden" name="action" value="login_action"/>
3.在Action中解析表单数据
public void doLogin(@FormGroup("login")Manager manager, Navigator nav, HttpSession session){
Manager existedManager=managerMapper.selectManager(manager.getName(),manager.getPswd());
if(existedManager!=null){
session.setAttribute("manager",existedManager);
nav.redirectTo("app1Link").withTarget("manager/home");
}
}
4.在Action中解析URL参数
public class MyAction{
@Autowired
HttpServletRequest request;
@Autowired
HttpServletResponse response;
@Autowired
ManagerMapper managerMapper;
public void deleteUser(Navigator nav){
long id=Long.parseLong(request.getParameter("id"));
Client client=clientMapper.selectClientById(id);
clientMapper.deleteClient(client);
nav.redirectTo("@app1Link").withTarget("manage/hint").withParameter("operation","删除");
}
}
5.通过url访问Screen
对于一个请求localhost:8081/manager/manage_user?id=1,
1)webx会先看有没有action参数,这里没有,所以跳过Action执行步骤;
2)webx会看action/manager/下有没有ManageUser类,若有,会执行其execute方法;
3)webx会查看template/manager/下有没有manageUser.vm模板,若有,渲染模板;
6.在Screen中获取url的参数
public class ModifyUser {
@Autowired
HttpServletRequest request;
@Autowired
ClientMapper clientMapper;
/**
* 获取modifyUser页面需要用到的信息
* @param context
*/
public void modifyUser(Context context){
long id=Long.parseLong(request.getParameter("id"));
Client client=clientMapper.selectClientById(id);
context.put("client",client);
}
}
6.在Screen中获取form的参数
public class ModifyUser {
@Autowired
HttpServletRequest request;
@Autowired
ClientMapper clientMapper;
/**
* 获取modifyUser页面需要用到的信息
* @param context
*/
public void modifyUser(Rundata rundata,TemplateContext context){
ParameterParser p = rundata.getParameters();
long clientId=p.getString("clientId");
context.put("clientId",clientId);
}
}
7.pipeline处理请求
pipeline控制了请求处理的流程。
<loop>
<choose>
<when>
<!-- 执行带模板的screen,默认有layout。 -->
<pl-conditions:target-extension-condition extension="null, vm, jsp, jspx" />
<performAction />
<performTemplateScreen />
<renderTemplate />
</when>
<when>
<!-- 执行不带模板的screen,默认无layout。 -->
<pl-conditions:target-extension-condition extension="do" />
<performAction />
<performScreen />
</when>
<otherwise>
<!-- 将控制交还给servlet engine。 -->
<exit />
</otherwise>
</choose>
对于一个具体的url,http://localhost:8081/user/get_user_name.do?userId=10000
在讲解代码实现之前,对上述的URL路径做下解析,WebX在处理该请求时,会对该路径进行解析,获取对应的Target,用于加载对应的处理方法。这里target后缀是.do,默认的会去查找对应的Screen/Action对象。查找时,对于该路径可能的两种解释是 user下的GetUserName对象或者是User对象的doGetUserName方法。WebX会先按第一中方法解析,失败后换第二种方法解析,如果再找不到对应的方法,报错(这个规则我是从网上看到的,但是对于第一种解析方法,我自己实践没有成功)。在之前的Screen对象中我们看到的对象方法往往只有一个 execute()方法,实际上在调用一个对象时,如果不指定对象的方法名,默认采用的就是execute方法。
1. <analyzeURL> - 分析URL
分析URL的目的是取得target,这里取得的target是collect_list.htm。如果用户访问的URL中并没有提供path信息,通常被理解为:用户想要访问“主页”。AnalyzeURL valve提供了一个可选的参数“homepage”,即是在这种情况下起作用。得到的target为“homepage”。
需要注意的是,target不代表模板名,也不代表类名。Target只是一个抽象的概念 —— 当前页面需要达成的目标。Target可能被后续的valves解释成模板名、类名或者其它东西。
2. 进入<choose> - 多重分支
很明显,“collect_list.htm”满足了第一个<when>所附带的条件:<target-extension-condition extension=" vm, jsp">,意思是target的后缀为“jsp”或为“vm”。
3. <performAction> - 执行action
和其它框架中的action概念不同,在Webx Turbine中,action是用来处理用户提交的表单的。由action变量的值决定处理表单的Action,
若未提供action参数,则跳过该步骤。
4. <performTemplateScreen> - 查找并执行screen。
如果target-extension为”do”,则没有<performTemplateScreen>这个valve,直接执行<performScreen>valve。
这里要用到一个规则:target映射成screen module类名的规则。
假设target为xxx/yyy/zzz,那么Webx Turbine会依次查找下面的screen模块:
screen.xxx.yyy.Zzz,
screen.xxx.yyy.Default,
screen.xxx.Default,
screen.Default。
本次请求的target为collection_list.htm,因此它会尝试查找screen.collection_list类。
如果找到screen类,Webx Turbine就会执行它。Screen类的功能,通常是读取数据库,然后把模板所需要的对象放到TemplateContext中。
如果找不到,也没关系 —— 这就是“页面优先”:一些页面没有业务逻辑,因此不需要screen类,只需要有模板就可以了。
5. <renderTemplate> - 渲染模板
这里用到两个规则:target映射成screen template,以及target映射成layout template。
假设target为xxx/yyy/zzz,那么Webx Turbine会查找下面的screen模板:/templates/screen/xxx/yyy/zzz。Screen模板如果未找到,就会报404 Not Found错误。找到screen模板以后,Webx Turbine还会试着查找下面的layout模板:
/templates/layout/xxx/yyy/zzz
/templates/layout/xxx/yyy/default
/templates/layout/xxx/default
/templates/layout/default
Layout模板如果找不到,就直接渲染screen模板;如果存在,则把渲染screen模板后的结果,嵌入到layout模板中。
Layout模板和screen模板中,都可以调用control。每个页面只有一个screen,却可以有任意多个controls。
6. <breakUnlessTargetRedirected> - 内部重定向
在screen和action中,可以进行“内部重定向”。内部重定向实质上就是由<breakUnlessTargetRedirected>实施的 —— 如果没有重定向标记,就退出;否则循环到<loop>标签。
和外部重定向不同,外部重定向是向浏览器返回一个302或303 response,其中包含Location header,浏览器看到这样的response以后,就会发出第二个请求。而内部重定向发生在pipeline内部,浏览器并不了解内部重定向。