Webx3學習筆記(2)——基本流程

Webx3項目是運行在jetty/tomcat這種Web應用容器中的,Web應用的模式都是請求-響應的。一個請求經過瀏覽器發出,封裝爲HTTP報文到達服務端,被容器接受到,封裝爲HttpRequest和HttpResponse等,而後進入Webx3的領域,經過Webx3的一套Pipeline機制到達指定的後臺,調用Java類獲取數據或處理,渲染Velocity模板並返回到客戶端進行顯示。簡單示意圖以下:php

未命名圖片

pipeline:html

未命名圖片

下面經過分析 Webx3學習筆記(1)——Hello, World!中生成的源碼來理解Webx3的基本流程:前端

以http://localhost:8081/?home 這個頁面做爲起點吧,經過觀察源碼可知這個頁面是結合了app1/templates/layout/default.vm和app1/templates/screen/index.vm兩個模板進行渲染出來的(怎麼找到這兩個頁面的我暫時也不清楚)。這個頁面就是一系列簡單demo的集合,下面挑有表明性的幾個進行分析:java

無模板Screen

url: http://localhost:8081/simple/say_hi.doweb

先看一下pipeline.xml中比較重要的loop部分,除了做者自己的註釋,也加上了我本身的理解:json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
< pl-valves:loop >
< pl-valves:choose >
< when >
<!-- 執行帶模板的screen,默認有layout。 -->
< pl-conditions:target-extension-condition extension = "null" />
 
   <!-- <strong>會去查找action包下面指定類的指定方法</strong> -->
   < pl-valves:performAction />
   <!-- <strong>會去模塊的screen包下面尋找同名的Screen類</strong>-->
   < pl-valves:performTemplateScreen />
   <!-- <strong>會去app1/templates/screen下尋找同名的vm模板,而後再找app1/templates/layout下同名的layout模板,最後把layout和screen模板拼起來進行渲染並返回,要是實在找不到layout,就只渲染screen。</strong>
-->
   < pl-valves:renderTemplate />
</ when >
< when >
<!-- 執行不帶模板的screen,無layout。 -->
< pl-conditions:target-extension-condition extension = "do" />
   <!-- 同上 -->
   < pl-valves:performAction />
   <!-- <strong>基本同上,不過這個步驟是必須的,沒法跳過。並且這種Screen類是有輸出的,相似Servlet</strong> -->
   < pl-valves:performScreen />
</ when >
< when >
<!-- 建立JSON,無模板,無layout。 -->
   < pl-conditions:target-extension-condition extension = "json" />
   < pl-valves:performScreen />
   < pl-valves:renderResultAsJson />
</ when >
< otherwise >
<!-- 將控制交還給servlet engine。 -->
< pl-valves:exit />
</ otherwise >
</ pl-valves:choose >
<!-- 假如rundata.setRedirectTarget()被設置,則循環,不然退出循環。 -->
< pl-valves:breakUnlessTargetRedirected />
</ pl-valves:loop >

本例中請求的後綴是.do,因此會執行:瀏覽器

1
2
3
4
5
< when >
< pl-conditions:target-extension-condition extension = "do" />
< pl-valves:performAction />
< pl-valves:performScreen />
</ when >

performAction是表單提交纔會執行的流程,因此這裏直接略過,執行到了performScreen,由於target爲simple/say_hi.do,因此該方法去尋找app1.module.screen.simple下尋找同名Java類,並且有一個命名轉換規則:say_hi->SayHi。因而咱們就找到了SayHi.java,並調用其execute()方法。tomcat

1
2
3
4
5
6
7
8
9
10
11
public class SayHi {
@Autowired
private HttpServletResponse response;
public void execute() throws Exception {
// 設置content type,但不須要設置charset。框架會設置正確的charset。
response.setContentType( "text/plain" );
// 如同servlet同樣:取得輸出流。
PrintWriter out = response.getWriter();
out.println( "Hi there, how are you doing today?" );
}
}

【注意對於第7個例子,say_hello_1.do中沒有找到execute()方法,就默認去找了doPerform(),say_hello_1/chinese.do,則會去調用其doChinese()方法。這是種不太經常使用的方式。】app

有模板Screen+表單處理

url:http://localhost:8081/form/register.htm框架

url後綴名爲.htm,會被轉換爲無後綴的形式,進行以下處理流程:

1
2
3
< pl-valves:performAction />
< pl-valves:performTemplateScreen />
< pl-valves:renderTemplate />

這也不是一個表單請求,因此略過performAction,執行performTemplateScreen ,去app1.module.screen.form包下尋找Register.Java,並無找到,可是也不要緊【performTemplateScreen 狀況下,Screen的Java類並非必須的】,繼續執行renderTemplate,去app1/templates/screen/form/下尋找register.vm,此次找到了,再結合app1/templates/layout/default.vm進行渲染,default.vm就是一個html骨架,主要使用register.vm進行填充。

register.vm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$page .setTitle( "Register" )
<form action= "$app1Link.setTarget(" form/register ")" method= "post" >
  $csrfToken .hiddenField
  <input type= "hidden" name= "action" value= "register_action" />
  #set ( $group = $form .register.defaultInstance)
  <p>Hello, what's your name?</p>
  # if (! $group .name.valid)
    <p> $group .name.message</p>
  # end
  <p>
    <input type= "text" name= "$group.name.key" value= "$!group.name.value" />
    <input type= "submit" name= "event_submit_do_register" />
  </p>
</form>

這樣就在前端看到了這樣的界面:
未命名圖片

其中有不少細節值得注意,讓咱們來關注一下register.vm:

表單的主體部分:

1
2
3
4
5
6
7
8
</pre>
<pre>#set ( $group = $form .register.defaultInstance)</pre>
<pre><p>Hello, what's your name?</p></pre>
<pre># if (! $group .name.valid)
  <p> $group .name.message</p>
  # end
  <input type= "text" name= "$group.name.key" value= "$!group.name.value" /></pre>
<pre>

$form.register是在WEB-INF/app1/form.xml裏定義的:

1
2
3
4
5
6
7
8
9
</pre>
<pre><group name= "register" extends = "csrfCheck" >
  <field name= "name" displayName= "你的名字" >
  <fm-validators:required-validator>
  <message>必須填寫 ${displayName}</message>
  </fm-validators:required-validator>
  </field>
  </group></pre>
<pre>

這個名爲register的FormGroup有一個屬性名爲name,該屬性還有一個erquired-validator,意思就是必填項。因此在表單內容填寫完畢後就會把值付給register的對應屬性傳到action那裏進行處理。

而後看action部分,若是咱們把<from action=」…」>中的action屬性給刪除掉,按理說提交表單時就會報錯,但是並無發生這樣的狀況,原來Webx3中的表單action是在這裏進行配置的:

1
2
3
4
5
6
</ pre >
< pre >$csrfToken.hiddenField
  < input type = "hidden" name = "action" value="<strong>register_action</ strong >"/>
  ……
  < input type = "submit" name="event_submit_<strong>do_register</ strong >"/></ pre >
< pre >

加粗部分共同決定了這個表單提交時會去找app1.module.action包下面的RegisterAction.doregister()方法。讓咱們去看看這個方法:

1
2
3
4
5
6
</pre>
<pre> public void doRegister( @FormGroup ( "register" ) Visitor visitor, Navigator nav) {
  String name = visitor.getName();</pre>
<pre>nav.redirectTo( "app1Link" ).withTarget( "form/welcome" ).withParameter( "name" , name);</pre>
<pre>}</pre>
<pre>

@FormGroup就是剛剛說過的,這裏會讀取其屬性並注入到Visitor中。Navigator是個內置的類,直接用就好了。
這裏在接收到表單請求後會把應用重定向到form/welcome去,同時帶一個參數name,就是剛剛表單中讀取的值。

而後form/welcome會走一樣的流程:

1
2
3
4
< pl-valves:performAction />
  < pl-valves:performTemplateScreen />
  < pl-valves:renderTemplate /></ pre >
< pre >
action略過,performTemplateScreen會找到app1.module.screen.form.Welcome.java:
1
2
3
4
5
</pre>
<pre>public void execute(@Param("name") String name, Context context) {
  context.put("name", name);
  }</pre>
<pre>

@Param(「name」) String name 等價於String name = HttpRequest.getParameter(「name」); 這樣寫能省幾行代碼。
這個類的做用僅僅是把request裏的name參數寫進Velocity的context裏面。

而後renderTemplate會找到app1/template/screen/form/welcome.vm:

1
2
3
4
</pre>
<pre> $page .setTitle( "Welcome, $name" )
  <p>Welcome, $name !</p></pre>
<pre>

一樣是結合layout渲染輸出。

最終展現的界面爲:

未命名圖片

至此一個表單的歷程已經所有走完。

相關文章
相關標籤/搜索