前面我们实现了 MVC 的最基本的功能,视图跳转以及基本数据的传递。这部分我们对 Controller 的返回值进行扩展,支持返回 字符串, JSon 以及 对Freemarker模板引擎的支持。
@ResponseBody 当Controller的方法 ResponseBody 注解的时候说明放回的值既是为 String 的话,也不是跳转到View。它的 value 为 ResponseType 枚举,用来表示 Response 返回内容的类型。
@ResponseTemplate 表示Controller方法返回时调用模板引擎来返回静态页面。他的value的值为模板页面的路径,type值表示为哪一种模板引擎。
当然,为了方便处理,一个Controller 方法不能同时拥有 这两个注解。
@Target({ ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ResponseBody { ResponseType value(); }
@Qualifier @Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ResponseTemplate { String value() default ""; TemplateType type(); }
我们对保存Controller方法的元数据对象扩展,添加这个两个注解的属性。
public class ControllerMethod { private final String prefix; private final String suffix; private final RequestMethod[] requestMethod; private final Method method; private final List<Map<String,Class<?>>> args; private final ResponseBody responseBody; private final ResponseTemplate responseTemplate; // gets, sets }
我们在拿到Controller方法的返回值之后,我们来判断这个方法应该是返回什么样的数据类型来进行不同的处理。
if (context.hasBodyAndTemplate()) { throw new ControllerException("Controller method can not has both ResponseBody and ResponseTemplate"); } if (context.isOutView()) { handleResponse(context); } else { handleResponseNotView(context); }
private void handleResponseNotView(RequestContext context) throws IOException { if (context.isResponseBody()) { try { responseBodyHandler.handler(context); } catch (JSONException e) { e.printStackTrace(); } } if(context.isResponseTemplate()){ templateHandler.handle(context); } }
在处理ResponseBody类型的时候,我们先根据注解的值来确定是否需要先处理成JSon 或者xml 等形式的数据,然后返回。我这里处理JSon直接使用了 json-lib.jar 。
public class DefaultResponseHandler implements ResponseBodyHandler{ public void handler(RequestContext context) throws IOException, JSONException { ResponseType responseType = context.getMethod().getResponseBody().value(); if(responseType.equals(ResponseType.TEXT_HTML)){ handlerText(context.getOutcome(), context); }else if(responseType.equals(ResponseType.APPLICATION_JSON)){ handlerJson(context.getOutcome(), context); } } private void handlerText(Object obj, RequestContext context) throws IOException{ context.getResponse().setContentType("text/html"); context.getResponse().getWriter().write(obj.toString()); } private void handlerJson(Object obj, RequestContext context) throws IOException, JSONException{ context.getResponse().setContentType("application/json"); String jsonString = null ; if(obj instanceof Collection){ jsonString= JSONArray.fromObject(obj).toString(); }else{ jsonString= JSONObject.fromObject(obj).toString(); } System.out.println(jsonString); context.getResponse().getWriter().write(jsonString==null? "":jsonString); } }
对于模板引擎的支持,Seam3 Rest 模块只能同时启用一种模板引擎。我这里打算同时支持多种模板引擎,所以在ResponseTemplate注解中加了TemplateType的,可以通过它的值来注入不同的模块引擎来处理。这功能还在完成中,下一部分会给出。这里直接使用了 Freemarker来处理。
public class DefaultTemplateHandler { @Inject @ResponseTemplate(type=TemplateType.FREEMARKER) private ResponeseTemplateHandler templateHandler; public void handle(RequestContext context){ try { templateHandler.process(context); } catch (IOException e) { e.printStackTrace(); } } }
@ResponseTemplate(type = TemplateType.FREEMARKER) public class FreeMarkerTemplate implements ResponeseTemplateHandler{ private Configuration cfg; public void init(String path) throws IOException { cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(path)); } public void process(RequestContext context) throws IOException { init(context.getRequest().getSession().getServletContext().getRealPath("/")); String templatePath = context.getMethod().getResponseTemplate().value(); Template t = cfg.getTemplate(templatePath); try { HttpServletResponse response = context.getResponse(); response.setContentType("text/html"); t.process(context.getOutcome(), context.getResponse().getWriter()); } catch (TemplateException e) { e.printStackTrace(); } } }
我们使用 ajax 来请求来或者这些返回值。先编写页面:
<html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <script type="text/javascript" src="resources/jquery-1.7.2.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $("#btn-string").click(function(){ $.ajax({ url: "cdi/person/str", success:function(data){ alert(data); $("#value-string").append(data); } }); }); $("#btn-json").click(function(){ $.ajax({ url: "cdi/person/json", success:function(data){ alert(data); $("#value-json").html(data.firstName+":"+data.lastName); } }); }); $("#btn-freemarker").click(function(){ $.ajax({ url: "cdi/person/freemarker", success:function(data){ alert(data); $("#value-freemarker").html(data); } }); }); }); </script> </head> <body> <input type="button" value="Ajax请求String" id="btn-string" /> <span id="value-string"></span> <br /> <input type="button" value="Ajax请求JSon" id="btn-json" /> <span id="value-json"></span> <br /> <input type="button" value="Ajax请求Freemarker" id="btn-freemarker" /> <span id="value-freemarker"></span> </body> </html>
编写PersonController.java
@Controller @RequestMapping("/person/") @Named @RequestScoped public class PersonController { @RequestMapping("str") @ResponseBody(ResponseType.TEXT_HTML) public String returnStr(){ return "Hello CDI"; } @RequestMapping("json") @ResponseBody(ResponseType.APPLICATION_JSON) public Person returnJson(){ return new Person(1L, "Feng", "J"); } @RequestMapping("freemarker") @ResponseTemplate(value="templates/user.ftl",type=TemplateType.FREEMARKER) public Map<String,Object> returnFreemarker(){ Map<String,Object> map = new HashMap<String, Object>(); List<Object> list = new ArrayList<Object>(); list.add(new Person(1L,"Feng" , "J")); list.add(new Person(2L,"CDI" , "MVC")); map.put("users", list); return map; } }
user.ftl
<#list users as user> <span style="color:red">${user.id}:${user.firstName}:${user.lastName}</span> </#list>
结果: