1. Controller类:Controller类为每个request产生一个实例,这样开发人员就不需要维护线程安全的代码。
2. 每个Controller里都被注入了一个log属性用于日志的处理,这个log属性是org.apache.commons.logging.Log类的一个实例,可以在Controller的每个action或method里直接使用,例如在catch到异常的时候,可以用 log.error('message', e)进行记录(Groovy把所有的异常都转换为运行时间异常,因此Groovy代码可以不catch它们,这是和Java开发所不同的)。
3. 慎重使用servletContext:servletContext可以在整个应用中共享数据,但是它不能区分客户端,不会自动被gc回收,也不是 synchronized,需要手工进行同步,如果大量使用会产生严重的性能瓶颈。因此,使用servletContext的原则是,在启动时设置好所有确实需要全局化的数据value,在运行时间只读取其中的数据,这样才能够通过unsynchronized方式访问它。
4. flash属性:flash对象是一个java.util.Map类的实例,访问方式和params对象一样,其特殊性在于存储位置。flash对象被保存在Flash Scope,只在下一个request有效,之后被自动清除。Flash Scope用于解决Controller进行redirect之后,产生了一个新的request,原先的request里保存的数据无法在新的 request里被访问到,而flash对象可以很好地解决这个在两个连续的request对象之间传递数据的问题。是否可以把一些数据存放在 session里呢?功能上是可以实现的,但是程序员必须自己清理这些临时数据,而flash则无需你费心。
5. params属性:params属性是grails自动注入到controller里的动态属性,它是一个request parameters构成的map,可以直接用params.keyItem的方式获取参数的值。
6. 数据绑定:params是一个map,可以直接被传递给domain类的constructor,如def album = new Album(params),来初始化domain类的所有属性;也可以通过album.properties = params的方式,利用Domain类内含的properties属性,对已有的Domain类实例进行修改;在data binding后的save()方法中,会自动调用validate()方法对绑定的数据进行校验,如果数据不合法,可以引用Domain类中的 errors属性进行错误判断和处理,grails也提供了缺省的处理方法。针对params产生的过程,如果数据绑定只是针对一个Domain类,那么在提供参数的Html中不需要考虑命名空间,如即可,如果涉及到多个Domain类的绑定,则需要在命名空间上区分开,如
相应的,在数据绑定过程中,也需要明确指定每个Domain类绑定的数据对应的命名空间:
def album = new Album(params["album"])
def artist = new Artist(params["artist"])
更高级的特性来自于grails提供的bindData()方法,通过该方法可以具体指定哪些参数和属性绑定,例如:
bindData(album, params, [include:"title"])
或
bindData(album, params, [exclude:"totalNumber"])
在涉及多个Domain类同时绑定的情况下,bindData()有第四个参数,指定对应的命名空间:
bindData(album, params, [include: "title"], "album")
在多个Domain类存在多对一、一对一等association的时候,通过提交的form表单中指定同时新增的关联Domain类属性,如:
如果是引用已有的关联Domain类,则可以指定其id:
当涉及到引用多个对象则形成了一个java.utill.Set,也可以修改缺省为java.util.List使其保持顺序:
类似的,如果不是新增对象,而是修改或引用已有对象,可以指定其id:
7. Command对象:除了不会被持久化,其他Domain类的特性都在Command对象中存在,Command对象通常可以和一个Domain对象对应,在Command对象中进行数据绑定和validate,Domain类只需要接收合法的属性然后持久化即可。
8. HTTP方法限制:某些情况下,需要限制Controller里的一些action不能被任意http请求调用,如通过HTTP GET不能产生删除数据的动作,这时可以使用Controller的allowedMethods属性来修改缺省的无限制约束,如:
def allowedMethods = [create:‘POST', delete: ['POST','DELETE']]
9. Controller的IO操作:Spring实现了 org.springframework.web.multipart.MultipartHttpServletRequest接口,提供了 getFile(),getFileMap(),getFileNames()等方法,以及 org.springframework.web.multipart.MultipartFile接口,提供了 getBytes(),getContentType(),getInputStream(),getName(),getSize(),transferTo() 等方法,可以管理文件的上传。对于上传文件的数据绑定,基于目标属性的类型,grails提供了智能化的类型转换。输出到客户端的二进制流也可以采用标准 servlet API调用去写HttpServletResponse对象,例如:
response.contentType = "application/octet-stream"
response.outputStream << zip
response.outputStream.flush()
10. 简单拦截(Interceptor):对于单个的Controller,可以采用Interceptor来进行拦截,拦截操作可以分为before, after和around,before是在action被执行之前被调用的代码,after是action执行之后调用,而around可以完全替换 action中的代码。例如:
def beforeInterceptor = {
log.trace("Executing action $actionName with params $params")
}
这样可以作为调试信息。
如果涉及多个Controller的拦截,则需使用Filter。
11. 测试Controller:可以使用mockDomain, mockRequest, mockResponse, mockSession, mockParams, mockFlash等属性来模拟动态加载的数据,对于render和redirect动作,可以使用renderArgs和redirectArgs中的属性进行比较,如:
assertEquals "show", renderArgs.view
对于Command对象,需要在测试前使用MockUtils.prepareForConstraintTests()方法激活它,然后才能new出一个实例进行测试。