rose手册第三章三节:controller层:ErrorHandler支持

rose 3.3.1 ErrorHandler的作用 一般来说传统的编程都会到处去try,特别是java里,try来try去的(如果你用erlang一定就知道,已经知道的可能性,怎么能叫异常?都try了还是让它崩了算了。。。)。
如果打开你的项目,每个java文件中的代码都有一堆的try,那这时候就是ErrorHandle上阵的时候了。
ErrorHanle致力于:统一捕捉和处理各种异常,可区分对待和返回;统一的出错体验。
非常类似做web开发时的500统一出错页面这样的东东。 3.3.2 示例

/**
* @author chenzhen@xiaomi.com
* 2010-12-1
*/

package com.chen.controllers;

import net.paoding.rose.web.ControllerErrorHandler;
import net.paoding.rose.web.Invocation;

public class ErrorHandler implements ControllerErrorHandler {

public Object onError(Invocation inv, Throwable ex) throws Throwable {

// TODO logger.error("handle err:", ex);

return "@error";
}
}

这是这么简单,不用怀疑!

3.3.3 放在哪里才能生效? 放在controllers目录下,和controller们在一起(幸福快乐地生活)。
一般来讲,ErrorHandler都是用在web项目里,在最快层起作用。
所有的方法都可以尽情地向处throws Exception了。
不需要再try了。

@Path("")
public class HelloController {
@Get("")
public String index2() throws Exception {
return "@hello world";
}
}

3.3.3 有用的例子: 不同的异常类型做不用的事情

/**
* @author chenzhen@xiaomi.com
* 2010-12-1
*/

package com.chen.controllers;

import net.paoding.rose.web.ControllerErrorHandler;
import net.paoding.rose.web.Invocation;

public class ErrorHandler implements ControllerErrorHandler {

public Object onError(Invocation inv, Throwable ex) throws Throwable {

// TODO logger.error("handle err:", ex);
if (ex instanceof RuntimeException) {
return "@runtime";
}
return "@error";
}
}

文中所提及代码均在 https://github.com/XiaoMi/rose/tree/master/rose-example 提供。
文档持续修改版本在 https://github.com/XiaoMi/rose/tree/master/chapter_3_3

Rose手册第三章二节:controller层:拦截器支持

rose 3.2.1 拦截器作用

面向切面编程(AOP)方法可以让一个项目更加关注核心逻辑,常见的一些最佳实践包括
权限
缓存
错误处理
延时加载
调试
持久化
资源池
等等。。。
而此处的拦截器目标是在controller层提供各种在controller执行前、执行后的代码切入,以达到各种可AOP的目标。
简单地说,拦截器能干的事情就是当你的项目写了一半时发现缺少啥全局要做的事情(比如需要验证权限),不用担心,搞一个拦截器就是了。

3.2.2 拦截器例子

public class AccessTrackInterceptor extends ControllerInterceptorAdapter {
public AccessTrackInterceptor() {
setPriority(29600);
}
@Override
public Class extends Annotation> getRequiredAnnotationClass() {
return PriCheckRequired.class; // 这是一个注解,只有标过的controller才会接受这个拦截器的洗礼。
}
@Override
public Object before(Invocation inv) throws Exception {
// TODO ....
return super.before(inv);
}

@Override
public void afterCompletion(final Invocation inv, Throwable ex) throws Exception {
// TODO ....
}

需要注意几点:

拦截器要放在controllers下(高级用法:打在rose-jar包里,参见5.1)
继承net.paoding.rose.web.ControllerInterceptorAdapter
按照实现的方法名,在controller执行前、中、后执行:
before:在controller执行前执行。
after:在controller执行中(后)执行,如果一个返回抛出了异常,则不会进来。
afterCompletion:在controller执行后执行,不论是否异常,都会进来。
isForAction:定义满足某条件的才会被拦截。

3.2.3 拦截器可动的位置细节

上面都讲得差不多了,实际上还有不少地方可以拦截的:
isForDispatcher:根据响应的情况判断是否拦截,比如说是正常请求、内部forward、还是include (但是没用过)
setPriority:设置一个数字表示拦截优先级,当有多个拦截器时,要精准控制,数字小的内层,大的在外层,在最外层的before方法最先执行,大家都执行完后它的after才最后执行。
round:这才是真正的controller执行中执行,不过用得很少。
getRequiredAnnotationClass:返回一个Annotation class name,表示这个拦截器只对此Annotation标过的controller才生效。常用。

3.2.4 实际应用场景

全站是否登录判断相关的逻辑,写在一个拦截器里,一次完成后,其他地方不再关心这个代码,在需要登录才能做的controller上注解一下,表示需要被执行拦截。
日志收集的逻辑,在一个拦截器里进行当前的access log记录。
权限体系的逻辑,写在一个拦截器里,在对应的作上作注解,拦截器中进行细节的判断,新加的api也只是需要一次注解就得到了权限的判断。

[文中所提代码均在 https://github.com/XiaoMi/rose/tree/master/rose-example]
常期更新版本在:https://github.com/XiaoMi/rose/tree/master/chapter_3_2

Rose手册第三章一节:controller层:url对照规则与返回结果规则

rose框架

3.1.1) url对照规则——最简单的例子

先看看怎样把url和某个方法对应起来。为了方便说明,现在我们来一起完成一个极简版的贴吧。
1)贴吧需要什么功能?

贴吧中当然会有很多“主帖”(topic),“主帖”下会有很多“跟帖”(comment)。
一般,贴吧中最基本的,会有下面这几个功能需要我们完成:

显示主帖列表
显示单个主帖和它的跟贴
显示单个跟贴
创建一个主帖
创建一个跟贴

2)设计 web API

然后让我们来规划一个REST风格的 web API :(“GET”和“POST”是指HTTP1.1中的请求方法)

显示主帖列表
GET http://github.com/myforum/topic
显示单个主帖和它的跟贴
GET http://github.com/myforum/topic/123
显示单个跟贴
GET http://github.com/myforum/topic/123/comment/456
创建一个主帖
POST http://github.com/myforum/topic
创建一个跟贴
POST http://github.com/myforum/topic/123/comment

可以发现一个共同点,所有API中,URI部分的第一级都是“/myforum”(但这并不是规定,仅仅为了演示)。
3)实现 web API

首先新建一个类,这个类的类名必须以“Controller”结尾:

@Path("myforum")
public class ForumController {
}

注意标注在类(class)上的注解“@Path("myforum")”,这意味着,这个类中定义的所有API的URI,都必须以“myforum”开头,比如“/myforum/xxx”和“/myforum/yyy”等(但“myforum”不一定是整个URI的第一级,比如“/aaa/myforum/bbb”)。

接着,实现第一个API——“GET http://github.com/myforum/topic”:

@Path("myforum")
public class ForumController {
@Get("topic")
public String getTopics() {
//显示主帖列表
return "topiclist";
}
}

因为是“GET”方法,所以在该方法上标注“@Get("")”,URI“/myforum/topic”中的“myforum”已经在“@Path("myforum")”中定义过了,所以只剩下“topic”,于是写“@Get("topic")”。

再看第二个API——“GET http://github.com/myforum/topic/123”。
跟前一个的唯一区别是,后面多了个“/123”,表示主帖id,而这个id当然不是固定的,只有用户点击链接发来请求时才能知道,肿么办?
没关系,rose支持正则表达式!可以这么写:

@Get("topic/{topicId:[0-9]+}")
public String showTopic(@Param("topicId") int topicId) {
//显示单个主帖和它的跟贴
return "topic";
}

与前一个API相比,多了段“/{topicId:[0-9]+}”。正则表达式被大括号"{}"包围,格式为“{ paramName : regularExpression }”,只有请求的URI能被正则表达式匹配时,才会执行这个方法,而被匹配的值将被保存在名为“topicId”的参数中。

同理,实现第三个API,稍微复杂一点:

@Get("topic/{topicId:[0-9]+}/comment/{commentId:[0-9]+}")
public String showComment(@Param("topicId") int topicId, @Param("commentId") int commentId) {
//显示单个跟贴
return "comment";
}

最后两个API使用POST方法,其他与前面相同:

@Post("topic")
public String createTopic(){
//创建一个主帖
return "topic";
@Post("topic/{topicId:[0-9]+}/comment")
public String createComment(@Param("topicId") int topicId){
//创建一个跟贴
return "comment";
}

完整的代码如下(省略了import语句):

@Path("myforum")
public class ForumController {

@Get("topic")
public String getTopics() {
//显示主帖列表
return "topiclist";
}

@Get("topic/{topicId:[0-9]+}")
public String showTopic(@Param("topicId") int topicId) {
//显示单个主帖和它的跟贴
return "topic";
}

@Get("topic/{topicId:[0-9]+}/comment/{commentId:[0-9]+}")
public String showComment(@Param("topicId") int topicId, @Param("commentId") int commentId) {
//显示单个跟贴
return "comment";
}

@Post("topic")
public String createTopic(){
//创建一个主帖
return "topic";
}

@Post("topic/{topicId:[0-9]+}/comment")
public String createComment(@Param("topicId") int topicId){
//创建一个跟贴
return "comment";
}
}

至此,一个贴吧功能的Controller就编写完成了。
4) 更多细节

除了上面例子中的做法(@Path(""),@Get("")和@Post("")),还可以通过包路径来规划URI。

比如前面例子中的Controller,在API不变的前提下,还可以这么做:

1.在controllers路径下新建一个叫做“myforum”的文件夹。
2.将ForumController从“xxx.controllers”移动到“xxx.controllers.myforum”
并改成下面这样:

@Path("")
public class ForumController {
@Get("topic")
public String getTopics() {
//显示主帖列表
return "topiclist";
}
... ...
}

只是将“@Path("myforum")”改成了“@Path("")”。这样做的好处是可以让项目中的代码组织清晰。
3.1.2) 返回结果规则
___NOT EOF___

author:vaporz

完整和长期修改版本在:https://github.com/XiaoMi/rose/tree/master/chapter_3_1 关于rose手册计划:https://github.com/XiaoMi/rose

Rose手册第二章:配置与使用

ROSE

2.1 基础环境

* 普通的pc机,del 380
* ubuntu 10.04基本不升级
* java version "1.6.0_29"
* eclipse
* m2clipse
* 茶一杯

2.2 maven简介

* maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。如果你已经有十次输入同样的Ant targets来编译你的代码、jar或者war、生成javadocs,你一定会自问,是否有一个重复性更少却能同样完成该工作的方法。Maven便提供了这样一种选择,将你的注意力从作业层转移到项目管理层。Maven项目已经能够知道如何构建和捆绑代码,运行测试,生成文档并宿主项目网页。
* maven对一个项目进入了固定的默认目录定义:
* src/main/java 写主要的java实现
* src/main/resources 写主要的配置文件
* src/test/java 写test case
* src/test/resources 写test case所需要的配置文件
* src/main/webapp [war项目特有]web项目的对外目录
* src/main/webapp/WEB-INF [war项目特有]web项目配置web.xml目录

2.3 项目建立

* 打开eclipse(需要提前安装好m2clipse插件)
* new -> other -> maven -> maven project
* create a simple project
* next
* group id:com.54chen
* artifact id:rose-example
* packaging: war
* finished

2.4 基础配置三步走

1)点火:基础的pom文件 打开2.3建立好的项目,打开pom.xml,添加下面的段落到project中:

  1. <dependencies>  
  2.         <dependency>  
  3.             <groupId>com.54chen</groupId>  
  4.             <artifactId>paoding-rose-scanning</artifactId>  
  5.             <version>1.0-SNAPSHOT</version>  
  6.         </dependency>  
  7.   
  8.         <dependency>  
  9.             <groupId>com.54chen</groupId>  
  10.             <artifactId>paoding-rose</artifactId>  
  11.             <version>1.0-SNAPSHOT</version>  
  12.         </dependency>  
  13.   
  14.         <dependency>  
  15.             <groupId>com.54chen</groupId>  
  16.             <artifactId>paoding-rose-portal</artifactId>  
  17.             <version>1.0-SNAPSHOT</version>  
  18.         </dependency>  
  19.   
  20.         <dependency>  
  21.             <groupId>com.54chen</groupId>  
  22.             <artifactId>paoding-rose-jade</artifactId>  
  23.             <version>1.1-SNAPSHOT</version>  
  24.         </dependency>  
  25.     </dependencies>  

上述是rose环境最基础的依赖包。再添加一点常见的编译设置:

  1. <build>  
  2.     <resources>  
  3.         <resource>  
  4.             <directory>src/main/resources</directory>  
  5.             <includes>  
  6.                 <include>**/*.*</include>  
  7.             </includes>  
  8.         </resource>  
  9.     </resources>  
  10.     <plugins>  
  11.         <plugin>  
  12.             <groupId>org.apache.maven.plugins</groupId>  
  13.             <artifactId>maven-war-plugin</artifactId>  
  14.             <version>2.0.2</version>  
  15.             <configuration>  
  16.                 <webResources>  
  17.                     <resource>  
  18.                         <targetPath>WEB-INF</targetPath>  
  19.                         <filtering>true</filtering>  
  20.                         <directory>src/main/resources</directory>  
  21.                         <includes>  
  22.                             <include>**/*.xml</include>  
  23.                             <include>**/*.properties</include>  
  24.                         </includes>  
  25.                         <targetPath>WEB-INF</targetPath>  
  26.                     </resource>  
  27.                 </webResources>  
  28.             </configuration>  
  29.         </plugin>  
  30.         <plugin>  
  31.             <groupId>org.apache.maven.plugins</groupId>  
  32.             <artifactId>maven-compiler-plugin</artifactId>  
  33.             <configuration>  
  34.                 <source>1.6</source>  
  35.                 <target>1.6</target>  
  36.                 <fork>true</fork>  
  37.                 <verbose>true</verbose>  
  38.                 <encoding>UTF-8</encoding>  
  39.                 <compilerArguments>  
  40.                     <sourcepath>  
  41.                         ${project.basedir}/src/main/java  
  42.                        </sourcepath>  
  43.                 </compilerArguments>  
  44.             </configuration>  
  45.         </plugin>  
  46.         <plugin>  
  47.             <groupId>org.apache.maven.plugins</groupId>  
  48.             <artifactId>maven-surefire-plugin</artifactId>  
  49.             <configuration>  
  50.                 <!-- 忽略测试 -->  
  51.                 <skip>true</skip>  
  52.             </configuration>  
  53.         </plugin>  
  54.     </plugins>  
  55. </build>  

上述是编译设置,也是放在project段落里。

2)松离合:必不可少的web.xml 在src/main/webapp/WEB-INF文件夹下建立web.xml:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  5.     id="WebApp_ID" version="2.5">  
  6.     <display-name>rose-example</display-name>  
  7.     <welcome-file-list>  
  8.         <welcome-file>index.html</welcome-file>  
  9.         <welcome-file>index.htm</welcome-file>  
  10.         <welcome-file>index.jsp</welcome-file>  
  11.         <welcome-file>default.html</welcome-file>  
  12.         <welcome-file>default.htm</welcome-file>  
  13.         <welcome-file>default.jsp</welcome-file>  
  14.     </welcome-file-list>  
  15.       
  16.        
  17.     <context-param>  
  18.         <param-name>log4jConfigLocation</param-name>  
  19.         <param-value>/WEB-INF/log4j.xml</param-value>  
  20.     </context-param>  
  21.   
  22.     <listener>  
  23.         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
  24.     </listener>  
  25.     <filter>  
  26.         <filter-name>roseFilter</filter-name>  
  27.         <filter-class>net.paoding.rose.RoseFilter</filter-class>  
  28.     </filter>   
  29.     <filter-mapping>  
  30.         <filter-name>roseFilter</filter-name>  
  31.         <url-pattern>/*</url-pattern>  
  32.         <dispatcher>REQUEST</dispatcher>  
  33.         <dispatcher>FORWARD</dispatcher>  
  34.         <dispatcher>INCLUDE</dispatcher>  
  35.     </filter-mapping>   
  36. </web-app>  
3)踩油门:applicationContext.xml src/main/resources/applicationContext.xml是spring环境的油门,所有包的扫描和启动都在这里定义:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5.     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
  6.     http://www.springframework.org/schema/context    
  7.     http://www.springframework.org/schema/context/spring-context-2.5.xsd"  
  8.     default-lazy-init="true">  
  9.   
  10.     <!-- 自动扫描 -->  
  11.     <context:annotation-config />  
  12.     <context:component-scan base-package="com.chen">  
  13.     </context:component-scan>  
  14.        
  15. </beans>  

2.5 hello world
* 在src/main/java上右键 -> new -> package -> name: com.chen
* 在com.chen上右键 -> new -> package -> com.chen.controllers [controllers是rose框架默认的加载controller的package name]
* 在com.chen.controllers上右键 -> new -> class -> HelloController [*Controller是rose框架默认的controller层的class后缀]
* 打开HelloController这个类
* 在public class HelloController添加注解@Path("") [Path注解是rose框架提供的标识每个controller的对外访问时的基础路径]
* 在HelloController中添加方法

  1. /** 
  2.  * @author 54chen(陈臻) [chenzhen@xiaomi.com cc0cc@126.com] 
  3.  * @since 2012-4-10 上午11:14:46 
  4.  */  
  5. package com.chen.controllers;  
  6.   
  7. import net.paoding.rose.web.annotation.Path;  
  8. import net.paoding.rose.web.annotation.rest.Get;  
  9.   
  10. @Path("")  
  11. public class HelloController {  
  12.   
  13.     @Get("")  
  14.     public String index() {  
  15.         return "@hello world";  
  16.     }  
  17. }  

* [Get注解是rose框架提供的标识一个http访问是get还是post或者是其他,并且会将path与get中的字符串连接成一个url]
* 上述代码可以从浏览器访问:http://localhost/。
* 下述代码可以从浏览器访问:http://localhost/hello/world [注意path与get中的参数]。

  1. /** 
  2.  * @author 54chen(陈臻) [chenzhen@xiaomi.com cc0cc@126.com] 
  3.  * @since 2012-4-10 上午11:14:46 
  4.  */  
  5. package com.chen.controllers;  
  6.   
  7. import net.paoding.rose.web.annotation.Path;  
  8. import net.paoding.rose.web.annotation.rest.Get;  
  9.   
  10. @Path("/hello/")  
  11. public class HelloController {  
  12.   
  13.     @Get("world")  
  14.     public String index() {  
  15.         return "@hello world";  
  16.     }  
  17. }  

__EOF__

* 动态更新版本地址在:https://github.com/XiaoMi/rose/tree/master/chapter_2 * 文中所提及的代码在:https://github.com/XiaoMi/rose/rose-example