Mar 8, 2018 451 PM

作者:wendal

什么是视图?

视图的任务就是将入口函数的返回值(一个Java对象)渲染到 HTTP 响应流中。

现在 Nutz.Mvc 自带的主要视图有

  • JSP - 采用 JSP 模板输出网页
  • Redirect - 客户端重定向
  • Forward - 服务器端中转
  • Json - 将对象输出成 Json 字符串
  • void - 什么都不做
  • Raw - 二进制输出,图片输出,文件下载等
    当然你还可以根据需要开发你自己的视图实现类,定制自己的视图也非常简单,请参看本文 一节

快速入门:

入口函数返回值

前面提到的,视图(View)就是来处理入口函数的返回值的。 当然每个视图的实现类处理的方式不同。我们先来看看视图接口的源代码,非常简单:

  1. void render(HttpServletRequest req,
  2. HttpServletResponse resp,
  3. Object obj)
  4. throws Throwable;
  5. }

你要想创建自己的视图,你主要的工作就是实现这个接口。

  • 显然你的实现类能拿到这次 HTTP 请求的 request 和 response 对象
  • 参数 obj 就是你入口函数的返回
  • 如果你在 @Fail 里声明的视图, obj 就是抛出的异常对象
    这里还需要再强调一下 Nutz 所谓视图的概念:

一种视图就是一种将 Java 对象写入 HTTP 响应流的方式,谢谢

下面,我们就这个观点,再多举几个个例子,希望大家能通过这两例子,理解不同的视图处理同样的对象,可以有多大的差异。

即你声明:

  1. @Ok("jsp:xxxx")

的时候,无论入口函数返回什么对象,它会将其保存到request的 "obj" 属性中。比如

  1. @At
  2. @Ok("jsp:/test.jsp")
  3. public String test(){
  4. return "this is test";
  5. }

那么,你在 test.jsp 这个页面里,通过:

  1. <%=request.getAttribute("obj")%>

或者用 JSTL 的 EL

  1. ${obj}

都会直接输出字符串 "this is test"

JSP 视图的更多介绍请参看

比如 Json 视图

即你声明:

  1. @Ok("json")

的时候,无论入口函数返回什么对象,都会被 Json.toJson 函数变成字符串,直接写到HTTP 响应流里。

当然,有些对象,被 Json.toJson 有点麻烦,比如

  • InputStream
  • Reader
    等,这时候你用 Json 视图来输出本身也是自找别扭,建议你使用

Json 视图的更多介绍请参看 #JSON_视图

根据返回值决定

声明如下内容

  1. @Ok("re:jsp:/index")
  2. public String login(@Param("..")User user, ViewModel model) {
  3. // 检查登陆参数等等...
  4. if (ok) {
  5. return null; // 返回null, 则代表走默认视图 jsp:/index
  6. }
  7. model.setv("err-msg", "用户名或密码错误");
  8. return "jsp:/user/login";
  9. }

ViewZone 视图的更多说明请参阅 #根据返回值决定视图

怎样使用视图?

通过注解 @Ok 和 你可以为你的入口函数声明不同的渲染方式。当然,在你的逻辑非常复杂的情况下你可以从你的入口函数直接返回一个 View 对象。

通过注解

执行一个业务逻辑可能有两种结果

  • 成功,正常返回
  • 失败,特指入口方法抛出异常
    在每个入口函数上,你都可声明这两个注解

  • @Ok


  • 仔细观察,你会发现,这两个注解的值只能是一个字符串,那么怎么通过字符串,匹配到视图呢?

无论是 @Ok 还是 ,他们的值的格式总是:

  1. "视图类型:视图值"
  • 字符 ':' 将这个字符串隔断,左半部分是视图的类型,右半部分是视图的值。
  • 不同的视图类型,值的形式可能也是不一样的

直接返回 View 对象

你可以在你入口函数根据不同的条件,决定返回何种视图。如果你需要为你的视图声明要渲染的数据,请返回 org.nutz.mvc.view.ViewWrapper

比如你可以这样写:

  1. ...
  2. @At("/myurl")
  3. public View myFunction(@Param("type") type){
  4. if(type==0)
  5. return new ViewWrapper(new UTF8JsonView(), "It is zero!");
  6. if(type<10)
  7. return new ViewWrapper(new UTF8JsonView(), "It less than 10!");
  8. return new ViewWrapper(new JspView("mypage.showNumber"), type);
  9. }
  10. ...

内置的视图

通过 org.nutz.mvc.view.DefaultViewMaker, Nutz.Mvc 提供了一些内置视图, 你可以通过@Ok 和 注解声明在你的入口函数上

JSP 视图

视图的实现类为: org.nutz.mvc.view.JspView

  1. @Ok("jsp:pet.detail")

你可以不声明视图的值:

  1. @Ok("jsp")

那么会根据这个请求的地址来寻找 JSP 文件,比如请求:

将会使用 WEB-INF/pet/detail.jsp 来渲染 HTTP 输出流

使用 JSTL

如果你使用 JSTL,你还可以

  • 通过 ${msg.xxxx} 输出本地字符串,参看 本地化字符串更多的介绍
  • 通过 ${base} 输出当前应用的 ContextPath
  • 通过 ${obj.xxx} 输出要渲染对象的某个属性值
  • 你需要知道,JSP 视图,会将要渲染的对象存入 request 对象的一个属性里,属性名为 "obj"

JSP 文件的位置

有些人(比如我)喜欢把 JSP 文件统统放在 WEB-INF 目录里。但是更多人的习惯是将 JSP 放在 WEB-INF 外面。

  • 这个将对应 WEB-INF/abc/bbc.jsp
  1. @Ok("jsp:abc.bbc")
  • 这个将对应 abc/bbc.jsp
  • 这个也将对应 abc/bbc.jsp
  1. @Ok("jsp:/abc/bbc.jsp")

本地化字符串

在 Nutz.Mvc 入口函数里使用的 JSP 文件可以使用 ${base}${msg} 来获取应用的 URL 前缀以及本地字符串。

如果你希望放在 WEB-INF 外面如果还希望使用本地化字符串,那么你需要在 web.xml 额外声明一个 Fileter,请参考 使用过滤器一节。

JSON 视图

视图的实现类为: org.nutz.mvc.view.UTF8JsonView

一般的使用形式:

  1. @Ok("json")

会将入口函数返回的对象转换成 JSON 字符串

你可以对这个 JSON 字符串的格式进行更多的控制:

  1. @Ok("json:{quoteName:true, ignoreNull:true}")

视图的值就是: "{quoteName:true, ignoreNull:true}",这个JSON字符串会被转换成 JsonFormat 对象。如果你想了解更多的 Json 转换的介绍哦,请参看

格式控制支持5个常用模式的简写,与JsonFormat的几个快捷方法一一对应

  1. @Ok("json:full")
  2. @Ok("json:nice")
  3. @Ok("json:forlook")
  4. @Ok("json:compact")
  5. @Ok("json:tiny")

忽略特定属性

  1. // 忽略password和salt. locked的值是一个正则表达式
  2. @Ok("json:{locked:'password|salt'}")
  3. // 仅输出abc和def
  4. @Ok("json:{actived:'abc|dbf'}")

自动应用unicode

  1. @Ok("json:{autoUnicode:true}")
  2. // 还可以强制让unicode小写,某些变态的php需要这样
  3. @Ok("json:{autoUnicode:true, unicodeLower:true}")

支持jsonp,默认取请求参数中的callback作为回调名称

  1. @Ok("jsonp")

叠加配置. 因为本身就是json字符串,所以可以任意搭配

  1. @Ok("json:{autoUnicode:true,locked:'password|salt'}")

重定向视图

视图的实现类为: org.nutz.mvc.view.ServerRedirectView

一般的使用形式:

  1. @Ok("redirect:/pet/list.nut")
  2. 或者
  3. @Ok("redirect:/article/2009/10465.html")
  4. // redirect与>>完全等价
  5. @Ok(">>:/article/2009/10465.html")

它将按照给定的视图值,发送 HTTP 重定向命令到客户端.

内部重定向视图

视图的实现类为: org.nutz.mvc.view.ForwardView

一般的使用形式:

  1. @Ok("forward:/pet/list.nut")
  2. //或者
  3. @Ok("forward:/article/2009/10465.html")
  4. 同时也可以写成
  5. @Ok("->:/article/2009/10465.html")

当:后面不加值,或者不是以/开头的话,生成的路径将与Jsp视图类似,除了最后不加.jsp之外. 其实Jsp视图只是Forward视图的子类而已

  • 这个将对应 WEB-INF/abc/bbc
  1. @Ok("jsp:abc.bbc")
  • 这个将对应 abc/bbc
  1. @Ok("jsp:/abc/bbc")
  • 这个将对应 abc/bbc.jsp

    它将按照给定的视图值,服务器内部重定向到指定的地址.

    HTTP 返回码视图

    视图的实现类为: org.nutz.mvc.view.HttpStatusView

    使用形式

    空白视图

    视图的实现类为: org.nutz.mvc.view.VoidView

    使用形式

    1. @Ok("void")

    对 HTTP 输出流不做任何处理,空实现.

    从 Ioc 容器中获取的视图

    使用形式

    1. @Ok("ioc:myView")

    Raw视图

    视图的实现类为: org.nutz.mvc.view.RawView

    该视图将数据对象直接写入 HTTP 响应

    ContentType 支持几种缩写:

    • xml - 表示 text/xml
    • js - 表示application/javascript
    • json - 表示application/json
    • html - 表示 text/html
    • htm - 表示 text/html
    • stream - 表示 application/octet-stream
    • png — 表示 image/png
    • jpg - 表示 image/jpeg
    • webp - 表示 image/webp
    • 默认的(即 '("raw")' ) - 将采用 ContentType?=text/plain
      使用方式
    1. @Ok("raw")

    将方法的返回值直接写入流,数据对象可以是如下类型:

    默认设置resp的ContentType为text/plain,当然,你可以设置ContentType的值

    1. @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/json");
    @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");

    RawView2

    视图的实现类为: org.nutz.mvc.view.RawView2

    该视图用于已知长度的输入流进行断点续传,一般用户不会接触到.

    根据返回值决定视图

    视图的实现类为: org.nutz.mvc.view.ViewZone

    该视图允许带一个默认视图,当方法返回值为null时调用

    一般形式, 默认视图为VoidView

    1. @Ok("re")

    一般形式2, 自定义默认视图, 可以是re视图以外的任意可用视图

    1. @Ok("re:jsp:/index")

    通过ViewModel进行视图传值. 因为其他视图的行为一般以方法的返回值作为视图渲染所需要的值,而re视图的返回值用于决定最终使用的视图,所以需要额外的ViewModel传递值.ViewModel是可选参数, 若不需要就可以不声明.

    1. @Ok("re:jsp:/index")
    2. public String login(@Param("..")User user, ViewModel model) {
    3. // 检查登陆参数等等...
    4. if (ok) {
    5. return null; // 返回null, 则代表走默认视图 jsp:/index
    6. }
    7. // 登陆失败
    8. model.set("err-msg", "用户名或密码错误");
    9. return "jsp:/user/login";
    10. }

    注意, 入口的方法的ViewModel参数不可以重新赋值,否则无法传值.

    返回值所代表的视图,不需要与默认视图一致.

    视图参数填充

    内置的视图,继承与AbstractPathView的, 均支持给定参数, 通过内置的nutz.el实现

    1. @Ok("redirect:/pet/detail.nut?pid=${obj.id}")
    2. 或者
    3. @Ok("redirect:/article/2009/${p.articleId}.html")

    视图会填充占位符号。填充的规则是:

    • 如果占位符名称以 "obj." 开头,则表示应该用入口函数的返回对象的某一个字段来填充
      • "obj.name" 表示对象的 "name" 字段
    • 如果占位符名称以 "p." 开头,,用 HTTP 参数表的值来填充
      • "p.abc" 就表示 HTTP 参数中的 "abc" 参数
    • 如果占位符名称以 "req_attr." 开头,,用 req 的attr的值来填充
      • "req_attr.abc" 就表示 req.getAttribute("abc")
    • 如果占位符名称以 "session_attr." 开头,,用 session 的attr的值来填充
      • "session_attr.abc" 就表示 session.getAttribute("abc")
    • 如果参数表没有值,则用空字符串来填充
      下面举几个例子:
    1. @Ok(">>:/pet/detail.nut?pid=${obj.id}")
    2. 入口函数返回: Pet 对象
    3. 则取 pet.getId() 填充 ${obj.id}

    用 HTTP 参数填充

    1. @Ok(">>:/pet/detail.nut?pid=${p.id}")
    2. HTTP 参数表取 "id",填充
    3. 如果没有这个参数,入口函数返回什么,直接 toString() 以后填充
    4. 如果入口函数是 void 或者返回 null,则用空串填充

    根据不同的返回值进行填充,充分利用EL表达式的功能

    1. // 如果用户未登录,跳到登录页面(/login.jsp),否则进入用户个人主页(/WEB-INF/jsp/user/home.jsp)
    2. @Ok("jsp:${obj == null ? '/login' : 'jsp.user.home'}")
    3. public User home() {
    4. if (isLogined())
    5. return dao.fetch(User.class, uid);
    6. return null;
    7. }

    定制自己的视图

    通过 ("xxx:xxxx") 这种形式声明的视图非常简洁,易于理解。某些时候,你可能觉得 Nutz.Mvc 支持的视图类型不够,你喜欢其他的视图模板引擎,比如 FreeMarker。因此你希望你的 能写成这样:

    1. @Ok("freemaker:/pattern/pet/myTemplate")

    又或者,你希望你的能想这样来输出 PDF 文件:

    1. @Ok("pdf:/pdf/article")

    在视图层,总会有这样或者哪样的需求,对吗? 那么你可以自己来定制自己的视图规则:

    实现自己的视图

    实现 org.nutz.mvc.View 接口,比如:

    实现 Render 函数即可,第三个参数就是你的入口函数的返回

    实现自己的视图工厂

    实现 org.nutz.mvc.ViewMaker 接口,比如:

    1. public class PdfViewMaker implements ViewMaker{
    2. public View make(Ioc ioc, String type, String value){
    3. if("pdf".equalsIgnoreCase(type)){
    4. return new PdfView(value);
    5. }
    6. return null;
    7. }

    函数 make 如果返回 null,则表示你的这个视图工厂不知道如何构建这种视图。 Nutz.Mvc 会看看其他的工厂能不能创建这个视图。如果所有的工厂都不能创建这个视图,则会导致异常。

    将视图工厂整合进应用中

    在主模块上,通过注解 将你的工厂声明进整个应用即可。

    • 你可以声明多个 ViewMaker
    • 所有的视图工厂,必须有一个 public 的默认构造函数。

    官方扩展集nutzbook中集成第三方模板引擎的描述

    本页面的文字允许在和GNU自由文档许可证下修改和再使用。