视图的任务就是将入口函数的返回值(一个Java对象)渲染到 HTTP 响应流中。
现在 Nutz.Mvc 自带的主要视图有
当然你还可以根据需要开发你自己的视图实现类,定制自己的视图也非常简单,请参看 本文 #定制自己的视图 一节
// 重定向
@Ok(">>:/user/login.html") // 浏览器重定向到 /user/login.html
@Ok("->:/user/home.html") // forward,内部重定向到 /user/home.html
// 跳转到jsp页面
@Ok("jsp:jsp.user.home") // 内部跳转到 /WEB-INF/jsp/user/home.jsp
@Ok("jsp:/user/login.jsp") // 内部跳转到 /user/login.jsp
// 把入口方法返回值转为json字符串写入响应
@Ok("json")
@Ok("json:full")
@Ok("json:{locked:'password|salt'}")
// 直接设置为一个http status
@Ok("http:200")
@Fail("http:500")
// 任性返回任意文本
@Ok("raw")
// 返回图片对象,自动转为指定格式
@Ok("raw:png") // 返回值需要时BufferedImage
// 文件下载, 入口方法返回值需要一个File对象
@Ok("raw:pdf")
// 什么都不做,入口方法使用resp对象自行写响应
@Ok("void")
// 根据返回值确定行为
@Ok("jsp:${obj == null ? 'jsp.user.login' : 'jsp.user.home'}")
// 根据入口方法返回值(String类型)来确定,可以是re视图之外的任意合法视图值
@Ok("re")
if (...)
return ">>:/user/login.html";
else
return "jsp:jsp.user.home";
前面提到的,视图(View)就是来处理入口函数的返回值的。 当然每个视图的实现类处理的方式 不同。我们先来看看视图接口的源代码,非常简单:
public interface View {
void render(HttpServletRequest req,
HttpServletResponse resp,
Object obj)
throws Throwable;
}
你要想创建自己的视图,你主要的工作就是实现这个接口。
这里还需要再强调一下 Nutz 所谓视图的概念:
一种视图就是一种将 Java 对象写入 HTTP 响应流的方式,谢谢
下面,我们就这个观点,再多举几个个例子,希望大家能通过这两例子,理解不同的视图处理同样的 对象,可以有多大的差异。
即你声明:
@Ok("jsp:xxxx")
的时候,无论入口函数返回什么对象,它会将其保存到request的 "obj" 属性中。比如
@At
@Ok("jsp:/test.jsp")
public String test(){
return "this is test";
}
那么,你在 test.jsp 这个页面里,通过:
<%=request.getAttribute("obj")%>
或者用 JSTL 的 EL
${obj}
都会直接输出字符串 "this is test"
JSP 视图的更多介绍请参看 #JSP_视图
即你声明:
@Ok("json")
的时候,无论入口函数返回什么对象,都会被 Json.toJson 函数变成字符串,直接写到 HTTP 响应流里。
当然,有些对象,被 Json.toJson 有点麻烦,比如
等,这时候你用 Json 视图来输出本身也是自找别扭,建议你使用 #Raw视图
Json 视图的更多介绍请参看 #JSON_视图
声明如下内容
@Ok("re:jsp:/index")
public String login(@Param("..")User user, ViewModel model) {
// 检查登陆参数等等...
if (ok) {
return null; // 返回null, 则代表走默认视图 jsp:/index
}
// 登陆失败
model.setv("err-msg", "用户名或密码错误");
return "jsp:/user/login";
}
ViewZone 视图的更多说明请参阅 #根据返回值决定视图
通过注解 @Ok 和 @Fail 你可以为你的入口函数声明不同的渲染方式。当然,在你的逻辑非常复杂的情况下 你可以从你的入口函数直接返回一个 View 对象。
执行一个业务逻辑可能有两种结果
在每个入口函数上,你都可声明这两个注解
仔细观察,你会发现,这两个注解的值只能是一个字符串,那么怎么通过字符串,匹配到视图呢?
无论是 @Ok 还是 @Fail,他们的值的格式总是:
"视图类型:视图值"
你可以在你入口函数根据不同的条件,决定返回何种视图。 如果你需要为你的视图声明要渲染的数据,请返回 org.nutz.mvc.view.ViewWrapper
比如你可以这样写:
...
@At("/myurl")
public View myFunction(@Param("type") type){
if(type==0)
return new ViewWrapper(new UTF8JsonView(), "It is zero!");
if(type<10)
return new ViewWrapper(new UTF8JsonView(), "It less than 10!");
return new ViewWrapper(new JspView("mypage.showNumber"), type);
}
...
通过 org.nutz.mvc.view.DefaultViewMaker, Nutz.Mvc 提供了一些内置视图, 你可以通过 @Ok 和 @Fail 注解声明在你的入口函数上
视图的实现类为: org.nutz.mvc.view.JspView
@Ok("jsp:pet.detail")
将会使用 WEB-INF/pet/detail.jsp 来渲染 HTTP 输出流
你可以不声明视图的值:
@Ok("jsp")
那么会根据这个请求的地址来寻找 JSP 文件,比如请求:
/pet/detail.nut
将会使用 WEB-INF/pet/detail.jsp 来渲染 HTTP 输出流
如果你使用 JSTL,你还可以
${msg.xxxx}
输出本地字符串,参看 本地化字符串更多的介绍${base}
输出当前应用的 ContextPath${obj.xxx}
输出要渲染对象的某个属性值有些人(比如我)喜欢把 JSP 文件统统放在 WEB-INF 目录里。但是更多人的习惯是将 JSP 放在 WEB-INF 外面。
@Ok("jsp:abc.bbc")
@Ok("jsp:/abc/bbc")
@Ok("jsp:/abc/bbc.jsp")
在 Nutz.Mvc 入口函数里使用的 JSP 文件可以使用 ${base}
和 ${msg}
来获取应用的 URL 前缀 以及本地字符串。
如果你希望放在 WEB-INF 外面如果还希望使用本地化字符串, 那么你需要在 web.xml 额外声明一个 Fileter, 请参考 本地化字符串 使用过滤器一节。
视图的实现类为: org.nutz.mvc.view.UTF8JsonView
一般的使用形式:
@Ok("json")
会将入口函数返回的对象转换成 JSON 字符串
你可以对这个 JSON 字符串的格式进行更多的控制:
@Ok("json:{quoteName:true, ignoreNull:true}")
视图的值就是: "{quoteName:true, ignoreNull:true}"
,这个JSON字符串会被转换成 JsonFormat 对象。 如果你想了解更多的 Json 转换的介绍哦,请参看 Json 手册
格式控制支持5个常用模式的简写,与JsonFormat的几个快捷方法一一对应
@Ok("json:full")
@Ok("json:nice")
@Ok("json:forlook")
@Ok("json:compact")
@Ok("json:tiny")
忽略特定属性
// 忽略password和salt. locked的值是一个正则表达式
@Ok("json:{locked:'password|salt'}")
// 仅输出abc和def
@Ok("json:{actived:'abc|dbf'}")
自动应用unicode
@Ok("json:{autoUnicode:true}")
// 还可以强制让unicode小写,某些变态的php需要这样
@Ok("json:{autoUnicode:true, unicodeLower:true}")
支持jsonp,默认取请求参数中的callback作为回调名称
@Ok("jsonp")
叠加配置. 因为本身就是json字符串,所以可以任意搭配
@Ok("json:{autoUnicode:true,locked:'password|salt'}")
视图的实现类为: org.nutz.mvc.view.ServerRedirectView
一般的使用形式:
@Ok("redirect:/pet/list.nut")
或者
@Ok("redirect:/article/2009/10465.html")
// redirect与>>完全等价
@Ok(">>:/article/2009/10465.html")
它将按照给定的视图值,发送 HTTP 重定向命令到客户端.
视图的实现类为: org.nutz.mvc.view.ForwardView
一般的使用形式:
@Ok("forward:/pet/list.nut")
//或者
@Ok("forward:/article/2009/10465.html")
同时也可以写成
@Ok("->:/article/2009/10465.html")
@Ok("jsp:abc.bbc")
@Ok("jsp:/abc/bbc")
@Ok("jsp:/abc/bbc.jsp")
它将按照给定的视图值,服务器内部重定向到指定的地址.
视图的实现类为: org.nutz.mvc.view.HttpStatusView
使用形式
// 返回 HTTP 404 错误码
@Ok("http:404")
// 返回 HTTP 500 错误码
@Ok("http:500")
视图的实现类为: org.nutz.mvc.view.VoidView
使用形式
@Ok("void")
对 HTTP 输出流不做任何处理,空实现.
使用形式
@Ok("ioc:myView")
将从 Ioc 容器中获取 myView 对象。 它必须是一个 View,否则会发生转型错误。 通过这种形式,可以支持你可以在 Ioc 容器中定义自己的视图对象。
视图的实现类为: org.nutz.mvc.view.RawView
该视图将数据对象直接写入 HTTP 响应
ContentType 支持几种缩写:
使用方式
@Ok("raw")
将方法的返回值直接写入流,数据对象可以是如下类型:
null | 什么都不做 |
byte[] | 按二进制方式写入HTTP响应流 |
InputStream | 按二进制方式写入响应流,并关闭 InputStream? |
char[] | 按文本方式写入HTTP响应流 |
Reader | 按文本方式写入HTTP响应流,并关闭 Reader |
File | 文件下载 |
BufferedImage | 根据设置的Content-Type转为指定的图片格式 |
默认的 | 直接将对象 toString() 后按文本方式写入HTTP响应流 |
默认设置resp的ContentType为text/plain,当然,你可以设置ContentType的值
@Ok("raw:application/rtf")
同时,它也支持如下的缩写形式:
@Ok("raw:xml") | 等价于@Ok("raw:text/xml"); |
@Ok("raw:html") | 等价于@Ok("raw:text/html"); |
@Ok("raw:htm") | 等价于@Ok("raw:text/html"); |
@Ok("raw:stream") | 等价于@Ok("raw:application/octet-stream"); |
@Ok("raw:json") | 等价于@Ok("raw:application/x-javascript"); |
@Ok("raw:js") | 等价于@Ok("raw:application/x-javascript"); |
@Ok("raw:jpg") | 等价于@Ok("raw:image/jpeg"); |
@Ok("raw:png") | 等价于@Ok("raw:image/png"); |
@Ok("raw:webp") | 等价于@Ok("raw:image/webp"); |
视图的实现类为: org.nutz.mvc.view.RawView2
该视图用于已知长度的输入流进行断点续传,一般用户不会接触到.
视图的实现类为: org.nutz.mvc.view.ViewZone
该视图允许带一个默认视图,当方法返回值为null时调用
一般形式, 默认视图为VoidView
@Ok("re")
一般形式2, 自定义默认视图, 可以是re视图以外的任意可用视图
@Ok("re:jsp:/index")
通过ViewModel进行视图传值. 因为其他视图的行为一般以方法的返回值作为视图渲染所需要的值, 而re视图的返回值用于决定最终使用的视图,所以需要额外的ViewModel传递值. ViewModel是可选参数, 若不需要就可以不声明.
@Ok("re:jsp:/index")
public String login(@Param("..")User user, ViewModel model) {
// 检查登陆参数等等...
if (ok) {
return null; // 返回null, 则代表走默认视图 jsp:/index
}
// 登陆失败
model.set("err-msg", "用户名或密码错误");
return "jsp:/user/login";
}
注意, 入口的方法的ViewModel参数不可以重新赋值,否则无法传值.
返回值所代表的视图,不需要与默认视图一致.
内置的视图,继承与AbstractPathView的, 均支持给定参数, 通过内置的nutz.el实现
@Ok("redirect:/pet/detail.nut?pid=${obj.id}")
或者
@Ok("redirect:/article/2009/${p.articleId}.html")
视图会填充占位符号。填充的规则是:
下面举几个例子:
@Ok(">>:/pet/detail.nut?pid=${obj.id}")
入口函数返回: Pet 对象
则取 pet.getId() 填充 ${obj.id}
@Ok(">>:/pet/detail.nut?pid=${p.id}")
从 HTTP 参数表取 "id",填充
如果没有这个参数,入口函数返回什么,直接 toString() 以后填充
如果入口函数是 void 或者返回 null,则用空串填充
根据不同的返回值进行填充,充分利用EL表达式的功能
// 如果用户未登录,跳到登录页面(/login.jsp),否则进入用户个人主页(/WEB-INF/jsp/user/home.jsp)
@Ok("jsp:${obj == null ? '/login' : 'jsp.user.home'}")
public User home() {
if (isLogined())
return dao.fetch(User.class, uid);
return null;
}
通过 @Ok("xxx:xxxx") 这种形式声明的视图非常简洁,易于理解。某些时候,你可能觉得 Nutz.Mvc 支持的视图 类型不够,你喜欢其他的视图模板引擎,比如 FreeMarker。因此你希望你的 @Ok 能 写成这样:
@Ok("freemaker:/pattern/pet/myTemplate")
又或者,你希望你的能想这样来输出 PDF 文件:
@Ok("pdf:/pdf/article")
在视图层,总会有这样或者哪样的需求,对吗? 那么你可以自己来定制自己的视图规则:
实现 org.nutz.mvc.View 接口,比如:
public class PdfView implements View{
public void render(HttpServletRequest req, HttpServletResponse resp, Object obj){
// TODO 实现代码 ...
}
}
实现 Render 函数即可,第三个参数就是你的入口函数的返回
实现 org.nutz.mvc.ViewMaker 接口,比如:
public class PdfViewMaker implements ViewMaker{
public View make(Ioc ioc, String type, String value){
if("pdf".equalsIgnoreCase(type)){
return new PdfView(value);
}
return null;
}
}
函数 make 如果返回 null,则表示你的这个视图工厂不知道如何构建这种视图。 Nutz.Mvc 会看看其他的工厂 能不能创建这个视图。如果所有的工厂都不能创建这个视图,则会导致异常。
在主模块上,通过注解 @Views 将你的工厂声明进整个应用即可。