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 |