zoukankan      html  css  js  c++  java
  • Webx3学习笔记(2)——基本流程

    Webx3项目是运行在jetty/tomcat这种Web应用容器中的,Web应用的模式都是请求-响应的。一个请求通过浏览器发出,封装为HTTP报文到达服务端,被容器接受到,封装为HttpRequest和HttpResponse等,然后进入Webx3的领域,通过Webx3的一套Pipeline机制到达指定的后台,调用Java类获取数据或处理,渲染Velocity模板并返回到客户端进行显示。简单示意图如下:

    未命名图片

    pipeline:

    未命名图片

    下面通过分析 Webx3学习笔记(1)——Hello, World!中生成的源码来理解Webx3的基本流程:

    以http://localhost:8081/?home 这个页面作为起点吧,通过观察源码可知这个页面是结合了app1/templates/layout/default.vm和app1/templates/screen/index.vm两个模板进行渲染出来的(怎么找到这两个页面的我暂时也不清楚)。这个页面就是一系列简单demo的集合,下面挑有代表性的几个进行分析:

    无模板Screen

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

    先看一下pipeline.xml中比较重要的loop部分,除了作者本身的注释,也加上了我自己的理解:

    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()方法。

    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()方法。这是种不太常用的方式。】

    有模板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渲染输出。

    最终展示的界面为:

    未命名图片

    至此一个表单的历程已经全部走完。

  • 相关阅读:
    vue cli3 打包到tomcat上报错问题
    前端html转pdf
    原生js上传图片遇到的坑(axios封装)
    vue slot的使用(transform动画)
    vue购物车动画效果
    关于el-select 单选与多选切换的时候报错的解决办法
    vue html属性绑定
    关于element ui滚动条使用
    css3flex布局实现商品列表 水平垂直居中 上下布局
    vue 项目 路由 router.push、 router.replace 和 router.go
  • 原文地址:https://www.cnblogs.com/Vae1990Silence/p/4618660.html
Copyright © 2011-2022 走看看