cetia4学习笔记(2)

tou3921 2008-09-18
http://our.bizairshop.com/note/post/cetia4-2.html

前面说过,在无状态的web service环境,是不允许拿到session的,
所以调用 getSessionAttribute()和调用getRequestAttribute()方法
是一样的效果。 这一开始看起来很怪异,但是这样就可以使用单一的方法
同时处理web和web service两种请求,这有点像EL语言在JSP中,可以在
多个scopes中查询存储的attribute值。 有些人对这个策略可能感到不
适应,但是也有一些方法来绕开它 ( //@TODO )



RenderContext的attribute相关方法:

    public String render( RenderContext context )
    {
        SearchFilter filter = ( SearchFilter )
          context.getSessionAttribute( "topics_filter" );
        if( filter == null )
        {
          filter = new SearchFilter();

          context.setSessionAttribute( "topics_filter", filter );
        }

        List topics = getTopics( filter );

        context.setRequestAttribute( "topics", topics );

        return "display_topics";
    }
这段代码如果运行在有状态的web环境,则 session和request的attribute分别
被赋值,如果运行在无状态的web service环境,则 getSessionAttribute 始终
返回null,因为session设置不进去值

RenderContext还有一个方法getConfiguration(),可以得到init配置参数,该
方法比servelt API中的得到初始化参数方法更灵活。

RenderContext这个接口不能获取到任何servlet具体接口对象,因为它在设计上是
为了不绑定任何servlet/portlet实现,getSessionAttribute继承RenderContext接口,
实现了可以跟servlet相关的具体方法
getServletRequest() , getServletResponse(),  getServletSession(), getServletConfig(),
getServletContext()

上面代码的ServletRequestContext翻版:
  public String render( ServletRequestContext context )
    {
        HttpSession session = context.getServletSession();
        HttpServletRequest request = context.getServletRequest();
 
        SearchFilter filter = ( SearchFilter )
          session.getAttribute( "topics_filter" );
        if( filter == null )
        {
          filter = new SearchFilter();

          session.setAttribute( "topics_filter", filter );
        }

        List topics = getTopics( filter );

        request.setAttribute( "topics", topics );

        return "display_topics";
    }


这两段代码的区别是:后者对于传统web请求和web service请求都是都会访问到session。

2.7 自定义响应
大部分返回都是一个字符串,指向一个jsp/jspx的view,但有时候可能需要返回一个image,
一个pdf文档,或者sendRedirect()等自定义响应。

cetia4这样实现,在render方法返回null,并且调用context的一些方法。

    public String render( RenderContext context ) throws IOException {
   context.redirectTo( "http://www.acsinet-solutions.com" );  // 相当于HttpServletResponse.sendRedirect()方法
   return null;
    }
在portlet环境中,sendRedirect是被禁止的,所以上面方法会失效。

    public String render( RenderContext context ) throws IOException {
        context.display( "/index.jsp" );
        return null;
    }
RenderContext的display方法,相当于javax.servet.RequestDispatcher的forward()方法
在portlet环境中,相当于javax.portlet.PortletRequestDispatcher的include()方法。
display()方法会设置HTTP禁止读取缓存。

2.8 识别RequestType
RenderContext的getRequestType()方法返回请求类型,请求类型在com.acsinet_solutions.cetia4.controller.RequestType
中定义,如 RequestType.WEB , WS_STATELESS, WS (WS_WS_STATELESS的别名)

    public String render( RenderContext context )
    {
        String requestType = context.getRequestType();

        if( requestType.equals( RequestType.WEB ) )
          loadTopicsForWebEnvironment( context );
        else if( requestType.equals( RequestType.WS_STATELESS ) )
          loadTopicsForWebServiceEnvironment( context );
        else
          throw new IllegalArgumentException
             ( "Unknown request type: " + requestType );

        return "display_topics";
    }

上面的方法也可以改写成 isWebRequest() 和 isWebServiceRequest()
不写if代码也可以用annotation实现判断功能,是
com.acsinet_solutions.cetia4.controller.meta.Method annotation.

@Method( name="render", types=RequestType.WEB )
public String renderWeb( RenderContext context )
{
   loadTopicsForWebEnvironment( context );
 
   return "display_topics";
}
@Method( name="render", types=RequestType.WS_STATELESS )
public String renderWebService( RenderContext context )
{
   loadTopicsForWebServiceEnvironment( context );
 
   return "display_topics";
}
框架会将上面两个方法都视为“render()” 方法(通过name属性)
目前支持的type只有 RequestType.WEB和RequestType.WS_STATELESS。

如果上面的两种方法只定义一个,也是可以的,如果之定义RequestType.WEB
类型,则只接受传统的web访问。web service访问该资源,则返回异常。
因为只有一个类型的方法定义,name属性也不需要,如下:
@Method( types=RequestType.WEB )
public String render( RenderContext context )
{
  loadTopicsForWebEnvironment( context );
  return "display_topics";
}

2.10 错误页面和Log支持
  如果Cetia4的REST servlet的render()方法抛出异常,将会forward到一个错误view,
在传统web请求,错误view在 /WEB-INFO/html/error.jsp,web service请求的错误view
默认在 /WEB-INF/xml/error.jspx。 该路径受初始化参数“默认view路径”和“扩展名”的影响,
需要参考主题 Configuration

error.jsp参考例子:

<%@ taglib uri="http://acsinet-solutions.com/cetia"
        prefix="cetia" %>
<html>
  <head>
    <title>Error Page</title>
    <link rel="stylesheet"
      href="${ pageContext.request.contextPath }/css/style.css">
  </head>
  <body>
    <div align="center" class="error">
      !!! ERROR !!! An error occurred in the application, please
      contact your administrator.
    </div>
    <hr>
    <div class="item">
      <cetia:writeException/>
    </div>
  </body>
</html>

<cetia:writeException> tag在错误debug方法很有用。但是只有初始化参数
com.acsinet_solutions.cetia4.display_system_exceptions被设置成true,
(该参数在web.xml中可以设置,也可以通过其他方法配置Configuration)
才会打印出异常stack信息。

配置如下:
<context-param>
  <param-name>com.acsinet_solutions.cetia4.display_system_exceptions</param-name>
  <param-value>true</param-value>
</context-param>

如果需要更加简洁的exception信息,用如下 error.jspx

<?xml version="1.0" encoding="ISO-8859-1"?>

<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page"
            xmlns:c="http://java.sun.com/jsp/jstl/core">
   <jsp:directive.page contentType="application/xml" session="false"/>

   <error>
      <c:if test="${ _system_exception != null }">
        <exception>
          <object>${ _system_exception }</object>
          <message>${ _system_exception.message }</message>
        </exception>
      </c:if>
      <c:if test="${ pageContext.exception != null }">
        <exception>
          <object>${ pageContext.exception }</object>
          <message>${ pageContext.exception.message }</message>
        </exception>
      </c:if>
    </error>
</jsp:root>

_system_exception是在render方法中可能抛出的异常对象

不是在render方法中产生的异常同样会被抛出。例如,如果到servlet的无效的资源请求,
或者如果请求产生,但是框架不知道确定是哪个请求类型,异常同样会产生。
但是这样的异常不会被送到错误页面,而是会被REST servlet抛出,作为一个
javax.servlet.ServletException 或 java.lang.RuntimeException 异常,由容器处理
这样的异常,通常是改变返回http code。

不抛出异常同样可以转到错误页面。通过在render方法中返回LOAD_ERROR常量,该常量
在com.acsinet_solutions.cetia4.controller.ControllerConstants中定义(值为"#error") ,
例程:

    public String render( RenderContext context )  {
        context.setSystemException( new SomeKindOfException() );
        return LOAD_ERROR;
    }

3 Get 访问 path info
REST资源的级别制度可以用在嵌套资源。
http://example.com/forum/topics/23 这样的资源用
    public String render( RenderContext context, String id )
    {
        return "display_topic";
    }
    public String render( RenderContext context, int id )
    {
        return "display_topic";
    }
都可以处理。第二个参数可以是primitive类型(int),可以是简单类型的包装类(Integer)
或者String, 三种类型

render方法表示path info的参数可以不止一个。例如:
    public String render
        ( RenderContext context, String language, int code )
    {
        return "display_topic";
    }
这个方法会响应  http://example.com/forum/topics/en/23

框架作者建议path info的参数最好不要多于一个,特殊情况下两个即可。
如 http://example.com/forum/topics/23/messages 或者  http://example.com/forum/topics/23/messages/34 
这样的url处理最好用 netested modules处理 ( //@TODO )

3.1 可变深度的参数
例如:
    public String render( RenderContext context, List<String> path ) {
        return "view";
    }
    public String render( RenderContext context, String[] path )
    {
        return "view";
    }   
如果这个方法在files servlet,它可以处理以 http://example.com/forum/files 开头的任何url

    public String render
        ( RenderContext context, String user, List<String> path )
    {
        return "view";
    }
这个方法又会覆盖上面的方法

4. 可替换views
http://example.com/forum/topics?_method=summary 会执行如下方法:
   public String renderSummary( RenderContext context ) {
        return "display_topic_summary";
    }

http://example.com/topics/45?_method=updateForm 会执行如下方法:
  public String renderUpdateForm( RenderContext context, int id ) {
        return "display_topic_update_form";
    }

4.1 单表单多提交
    <form method="get" action="/forum/topics/45">
         ...
   
        <input type="submit" name="_method_search" value="   Search   ">
        <input type="submit" name="_method_view" value="   View   ">
    </form>
第一个提交按钮会调用同样资源的renderSearch()方法,第二个按钮会调用方法renderView().

原理是  /forum/topics/45?_method_search= 
/forum/topics/45?_method_view=  会被传递到同样的resource去。



5. POST, PUT 和 DELETE HTTP 请求
Cetia4 REST框架处理POST, PUT 和 DELETE HTTP 请求是通过action方法,跟java portlet API方法类似。
这些方法必须是public,除了以render开头外,可以是任何名字,他们必须至少带有一个
com.acsinet_solutions.cetia4.controller.ActionContext类的参数,而且返回String值,但是这个
返回值含义跟render方法的返回值完全不同。

例如:
    public String update( ActionContext context ) {
        return "topics/15";
    }

POST类型的http请求默认调用update()方法
PUT类型的http请求默认调用insert()方法
DELETE类型的http请求默认使用delete()方法

POST-->redirect Get模式 (POST-REDIRECT-GET pattern ( or Redirect After Post pattern ) ) 的探讨:
http://www.theserverside.com/patterns/thread.tss?thread_id=20936

POST/PUT/DELETE这些方法返回值在传统web请求中是相对于该资源的URI,然后请求会转向(redirect)到
这个返回值指向的资源。

这个返回值一般用相对路径,用绝对路径一般是转向其他域资源

同样的,跟render方法差不多,返回null表示方法内自己处理转发。例如:
    public String update( ActionContext context ) throws IOException {
        context.redirectTo( "http://example.com" );
        return null;
    }
   
5.1  Web Service 请求
    public String update( ActionContext context )
    {
        return "topics/15";
    }
web service的请求会忽略返回值,上面的方法,如果是web service请求,不会转向topics/15资源,而是转向
统一的update页面,默认是该资源对应目录的update.jspx文件,如/WEB-INF/html/topics/update.jspx

应答文件:
<?xml version="1.0" encoding="ISO-8859-1"?>

<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page">
  <jsp:directive.page contentType="application/xml" session="false"/>

  <response particular="true">
    <status>${ task.status }</status>
  </topics>
</jsp:root>

5.2 POST/PUT/DELETE HTTP请求的path info
 
  // 对应 http://example.com/forum/topics
    public String update( ActionContext context ) {
        return "topics";
    }
   
// 对应 http://example.com/forum/topics/46
    public String update( ActionContext context, int id ) {
        return "topics/15";
    }
   
    // 对应 http://example.com/forum/topics/es/456
    public String update( ActionContext context, String lang, int id ) {
        return "topics/en/15";
    }

/forum/topics
_method=insert  _method=delete   _method=consolidate

Global site tag (gtag.js) - Google Analytics