首先一个好的 API 一定是易于理解的,这样可以大大的减少开发者学习成本。Hasor 在设计上非常注重 API 的设计。功能的实现的优先级都没有 API 设计的优先级高,可见 Hasor 并不是简单的为了实现一个轮子而去设计,它是站在开发者角度来思考的。
前面我们在《https://my.oschina.net/u/1166271/blog/753001》一文中看到了几种处理方式,那么这一次在详细的扒一扒使用 Hasor 接受 Request 请求的那些事。
- 使用Filter拦截请求
Filter 是一种非常非常常见的接受请求方式,它有很多优点这里就不在仔细展开。一般我们要用到 Filter 就必须要在 web.xml 中注册它,或者标记 Servlet3.0 的注解进行注册。虽然可以实现 Filter 的注册。但是我们无法控制具体过程。如果 Filter 没有生效很难直接定位问题。
这个时候如果能够非常明确的在代码中加一个断点让我知道跑过这一行代码之后 Filter 就会被注册进去,那么我会很欣慰。Hasor 可以提供你这样的帮助,下面看例子:
public class StartModule extends WebModule {
public void loadModule(WebApiBinder apiBinder) throws Throwable {
...
apiBinder.filter("/*").through(new EncodingFilter());
// or
apiBinder.filter("/*").through(EncodingFilter.class);
...
}
}
如果你有多个 Filter 要注册到 环境中还可以为它们指定顺序。
apiBinder.filter("/*").through(0,new EncodingFilter());
apiBinder.filter("/*").through(1,new JumpFilter());
// or
apiBinder.filter("/*").through(0,EncodingFilter.class);
apiBinder.filter("/*").through(1,new JumpFilter());
如果你指定了顺序,就不需要太在意真实的插入顺序。因为 Hasor 会在初始化的时候自动为它们排序,如果你没有指定顺序,那么默认顺序都是0。
而 Filter 就是一个普通的 Filter,例:
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
...
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpReq = (HttpServletRequest) request;
final HttpServletResponse httpRes = (HttpServletResponse) response;
httpReq.setCharacterEncoding("utf-8");
httpRes.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
public void destroy() {
...
}
}
- 使用Servlet接受请求
下面再把传统的 Servlet 添加进来。怎么样是不是很简单呢?
public class StartModule extends WebModule {
public void loadModule(WebApiBinder apiBinder) throws Throwable {
...
apiBinder.serve("echo.data").with(MyServlet.class);
// or
apiBinder.serve("echo.data").with(new MyServlet());
...
}
}
使用 Hasor 提供的这种做法,可以大大减少对 web.xml 的配置需求。甚至你都可以做一些判断然后决定具体装载哪个 Filter 或者 Servlet。这样一来你的程序完全由你自己做主,你完全掌控所有细节。
即使出现问题,排查起来也非常方便。打打 log 就可以清楚的知道,在何时加载了什么东西。
- 使用 Controller 接受请求
这个功能稍微高级一些,它会默认拦截 “*.html、*.htm、*.do、*.json” 这四类请求。我们先看一个最简单的 Controller。
@MappingTo("/index.htm")
public class Index {
public void execute() {
System.out.println("Hello Request");
}
}
在这个例子中我们使用 @MappingTo 注解告诉 Hasor,这个 Controller 负责接收来自“/index.htm”的请求。当我们访问这个页面的时候,控制台会打印出“Hello Request”。
上面这个例子恐怕很难有什么实际使用场景,拿来最触发后台任务的入口还差不多。
下面我们改造一下这个 Controller 让它可以接收来自 Request 的请求参数。
@MappingTo("/index.htm")
public class Index {
public void execute(@ReqParam("name") String name) {
System.out.println("Hello Request ,the name is " + name);
}
}
这次我们增加了一个参数并且给参数标记上注解,告诉 Hasor,把名称为 name 的请求参数传递进来。然后我们把它打印到控制台中。再次访问这个地址并传入参数:“/index.htm?name=zyc”。
控制台上会打印出“Hello Request ,the name is zyc”。
接下来让我们在看一看稍微酷炫一点传参技能,把参数作为 int 传递进来。
@MappingTo("/index.htm")
public class Index {
public void execute(@ReqParam("name") String name, @ReqParam("age") int age) {
System.out.println("Hello Request ,the name is " + name + ", age is " + age);
}
}
请求链接“/index.htm?name=sss&age=30”,控制台上可以看到“Hello Request ,the name is sss, age is 30”。
下面再来一个更酷炫的传参数技能,把传入的参数自动转成 Date。
@MappingTo("/index.htm")
public class Index {
static {
DateConverter converter = new DateConverter();
converter.setPattern("yyyy-mm-dd");
ConverterUtils.register(converter, Date.class);
}
public void execute(@ReqParam("name") String name, @ReqParam("age") int age, @ReqParam("date") Date date) {
System.out.println("Hello Request ,the name is " + name + ", age is " + age);
System.out.println("date is " + date);
}
}
默认情况下 Hasor 是无法处理 String 到 Date 的转换。你会得到“DateConverter does not support default String to 'Date' conversion.”一个类型转换错误。有关这个错误的说明可以在这里看到:http://stackoverflow.com/questions/5757242/java-beanutilsbean-convert-date-to-string
因此我们有必要在访问这个 Controller 之前把初始化工作做好,在上面例子中我把它写在静态代码块里以确保可以被先初始化。
接下来我们访问URL:“http://localhost:8080/index.htm?name=sss&age=30&date=2016-09-22”,就会得到这个时间对象。
Hello Request ,the name is sss, age is 30
date is Fri Jan 22 00:09:00 CST 2016
怎么样还算酷炫吧。
通常一个请求有很多参数,如果像这样一个一个的排下去。实在是太不优雅了,可不可以把传入的参数都封装到一个Bean里,这样获取参数时候会很方便呢?
来,上例子,先做一个封装Bean
public class LoginForm {
@ReqParam("email")
private String email;
@ReqParam("account")
private String account;
@ReqParam("password")
private String password;
@ReqParam("redirectURI")
private String redirectURI;
...
}
然后使用这个封装 Bean 来接收请求参数。
@MappingTo("/login.do")
public class Longin {
public void execute(@Params LoginForm loginForm) {
System.out.println("login data is " + JSON.toString(loginForm));
}
}
接着请求“login.do”并且代入各种参数,接下来在控制台上就会看到这些参数都乖乖的躺在那里了。
请求地址:“/login.do?email=zyc@hasor.net&account=zyc&password=zycpwd”
- 传入参数数组
如果遇到页面上的 checkbox 参数传入是不是犯难了,你可能会想:“还是给我 Request 把”。
Hasor 对于这种要求早已想到了,来看下面这个例子:
@MappingTo("/arrayParams.htm")
public class ArrayParams {
public void execute(@ReqParam("name") String[] nameArray) {
System.out.println("Hello Request ,the name is " + JSON.toString(nameArray));
}
}
请求链接:“http://localhost:8080/arrayParams.htm?name=zyc&name=hahada”或者自己搞一个表单递交上来。
你会看到控制台上打印出“Hello Request ,the name is ["zyc","hahada"]” 参数也乖乖的躺在这里了。
- 我还是觉得 Request 更舒服一点
如果你觉得这一切还是不够,必须要拿到 Request!!那好吧,使用下面这样几种方式,你可以拿到心目中梦寐以求的 Request。
@MappingTo("/givemeRequest.do")
public class GiveMeRequest {
public void execute(RenderData data) {
data.getHttpRequest();
data.getHttpResponse();
}
// or
public void execute(HttpServletRequest request,HttpServletResponse response) {
}
}
或者使用继承类来拿到Request。
@MappingTo("/action.do")
public class Action extends WebController {
public void execute() {
this.getRequest();
this.getResponse();
}
}
继承类可不是简简单单的给你提供 Request/Response。通过继承类的“getPara”你可以可以拿到请求参数。WebController 的用法和 JFinal 是一样的,它的原身就是来自 JFinal。
怎么样亲切吧?