转载:http://coderbee.net/index.php/java/20140719/959

项目组用了 Spring MVC 进行开发,觉得对里面的使用方式不是很满意,就想,如果是我来搭建开发环境,我会怎么做?下面就是我的想法,只关注于 MVC 的 View 层。

一、统一的响应格式

现在基本上都是用 ajax 来调用后台接口,拿到 json格式的数据再展示,有的人直接返回数据,却没有考虑异常的情况,我觉得返回的报文里必须包含表示可能的异常信息的数据和业务响应数据。我定义了下面这个类来表示报文格式:

/**
* 统一的 HTTP 响应格式。<br/>
* code 为 "ok" 表示业务调用成功,否则是失败的错误码,如果有多个则以逗号分隔。<br/>
* data 是业务数据,如果失败了则是 null。
*
* @author http://coderbee.net
*
*/
public class RespBody {
public static final String OK_CODE = "ok";
private final String code;
private final Object data; private static final RespBody OK = new RespBody(OK_CODE, null); private RespBody(String code, Object data) {
this.code = code;
this.data = data;
} public static RespBody ok() {
return OK;
} public static RespBody ok(Object data) {
return new RespBody("ok", data);
} public static RespBody error(String code) {
return new RespBody(code, null);
} public static RespBody error(String code, Object msg) {
return new RespBody(code, msg);
} public String getCode() {
return code;
} public Object getData() {
return data;
}
}

这个类提供了一些静态方法来快速构建响应报文,这也是很重要的一个设计:用静态工厂方法而不是构造函数。

这里的 code 不应该是直接的错误提示信息,应该只是简单的错误编码,这样不同的客户端都可以调用这个 API,然后再根据错误编码、客户端语言和自己的客户端特性选择合适的错误提示信息和提示方式。

二、统一的异常处理

很多人都不考虑异常的情况,导致异常栈直接抛到响应里,这是不友好也不安全的。统一的异常处理是必须的。

我定义了一个 BaseController

@Controller
public class BaseController {
protected final static Logger logger = LoggerFactory
.getLogger("controller"); @ResponseBody
@ExceptionHandler(Exception.class)
public RespBody exceptionHandler(Exception ex) {
return RespBody.error("exception", ex.getMessage());
}
}

它的作用很简单,就是定义了一个统一的异常处理逻辑。Spring MVC对异常处理的逻辑很好,如果某个Controller类没有提供带 @ExceptionHandler 注解的方法,则会查找父类是否有这种方法,所以继承自这个 BaseController 的 Controller 都自动获得异常处理能力。

三、统一的参数校验

参数校验是必须的,而且必须放在服务器端来做,客户端的校验都是可以绕过的

Spring MVC 当然也支持参数校验,在 Spring MVC 的配置文件里加入 <mvc:annotation-driven /> 即可以开启注解校验。

但 Spring MVC 的参数校验有些局限:

  • 不支持对基本类型和 String 类型的参数进行校验,也就是只支持对 POJO 校验,这个非常不友好,如果一个接口只有很少的几个参数都必须定义一个 POJO 是很恼人的,要么就得手动校验,写一些 if 分支;
  • 每个POJO后面都得有一个 BindingResult 的参数,作为对 POJO 的校验结果。

而且在每个方法里都必须对 BindingResult 进行检测,来判断参数是否合法。

在 AOP 里进行参数校验

借助 Spring 对 AOP 的支持,我们可以在 AOP 里对请求的 Controller 的方法进行拦截,做参数校验,如果校验不合格,则直接返回(因为我们已经有了统一的响应格式)。

在 AOP 里,我们可以用 Hibernate-Validator 进行手动校验,而不是通过 Spring-MVC 进行校验,这样我们就不需要在每个 POJO 后面放一个 BindingResult 参数,且 Hibernate-Validator 支持对基本类型和 String 类型的参数进行校验。

下面的代码是在 Hibernate-Validator-4.2.0-Final、validation-api-1.0.0-GA 下测试的:

首先定义一个 BindingResultHandler 类,它的方法 validate 校验请求的 Controller 的方法的参数是否合格,如果合格则继续调用业务逻辑,否则返回错误提示。

public class BindingResultHandler {

    public Object validate(ProceedingJoinPoint pjp) throws Throwable {
Object target = pjp.getTarget(); MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
Method method = joinPointObject.getMethod(); MethodValidator validator = Validation
.byProvider(HibernateValidator.class).configure()
.buildValidatorFactory().getValidator()
.unwrap(MethodValidator.class); Set<MethodConstraintViolation<Object>> violations = validator
.validateAllParameters(target, method, pjp.getArgs(),
new Class[] {}); if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder(128);
for (ConstraintViolation<Object> violation : violations) {
sb.append(',').append(violation.getMessage());
}
return RespBody.error(sb.substring(1), "param validation failed .");
} return pjp.proceed();
}
}

AOP 配置:

<bean id="bindingResultHandler" class="net.coderbee.demo.controller.validation.BindingResultHandler" />
<aop:config>
<aop:aspect id="aspectBindingResult" ref="bindingResultHandler">
<aop:pointcut id="bindingResultHandlerPointcut"
expression="execution(public * net.coderbee.demo.controller..*Controller.*(..))" /> <aop:around method="validate" pointcut-ref="bindingResultHandlerPointcut" />
</aop:aspect>
</aop:config>

这样在 Controller 里就只需做注解不能完成的校验了。

@Controller
public class UserController extends BaseController {
@ResponseBody
@RequestMapping(value = "/test/valids")
public RespBody valids(@Valid User user, @Valid Address address) { return RespBody.ok(user);
}
}

这样的代码会简洁很多。

Spring MVC 与 web开发的更多相关文章

  1. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

  2. Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用

    前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...

  3. [翻译]Spring MVC RESTFul Web Service CRUD 例子

    Spring MVC RESTFul Web Service CRUD 例子 本文主要翻译自:http://memorynotfound.com/spring-mvc-restful-web-serv ...

  4. spring mvc构建WEB应用程序入门例子

    在使用spring mvc 构建web应用程序之前,需要了解spring mvc 的请求过程是怎样的,然后记录下如何搭建一个超简单的spring mvc例子. 1) spring mvc的请求经历 请 ...

  5. 使用IDEA和gradle搭建Spring MVC和MyBatis开发环境

    1. 概述 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具. 它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐 ...

  6. Spring + Spring MVC + Hibernate项目开发集成(注解)

    在自己从事的项目中都是使用xml配置的方式来进行的,随着项目的越来越大,会发现配置文件会相当的庞大,这个不利于项目的进行和后期的维护.于是考虑使用注解的方式来进行项目的开发,前些日子就抽空学习了一下. ...

  7. Spring基础系列-Web开发

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996902.html SpringBoot基础系列-web开发 概述 web开发就是集成 ...

  8. 译:7.使用Spring MVC服务Web内容

    本指南向您介绍了使用Spring创建“hello world”网站的过程.阅读原文:Serving Web Content with Spring MVC 1. 你将会构建什么? 您将构建一个具有静态 ...

  9. 跟我学Spring Boot(三)Spring Boot 的web开发

    1.Web开发中至关重要的一部分,Web开发的核心内容主要包括内嵌Servlet容器和SpringMVC spring boot  提供了spring-boot-starter-web 为web开发提 ...

随机推荐

  1. 比较X与Y的大小,绝对精准!!!!!!

    代码! #include<stdio.h> int max(int a,int b) { int x; x=a+b; return x; } int main() { int i,n,t; ...

  2. NSS_02 日志配置

    采用log4net,使用系统推荐的最新版本:log4net-1.2.11-bin-newkey.zip(网址:http://logging.apache.org/log4net/download_lo ...

  3. jQuery插件开发 - 其实很简单

    [前言] jQuery已经被广泛使用,凭借其简洁的API,对DOM强大的操控性,易扩展性越来越受到web开发人员的喜爱,我在社区也发布了很多的jQuery插件,经常有人询问一些技巧,因此干脆写这么一篇 ...

  4. ajax返回的json内容进行排序

    关键方法:sort()用于对数组的元素进行排序. return a.num-b.num是升序: return b.num-a.num;是降序 writeln在输出后面加\n,在文档里是换行,在html ...

  5. 符合web标准的网页下拉菜单

    <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN"> <head> < ...

  6. slider jq小插件

    html代码 <div class="r_list r_1" style="display:block;"> <div class=" ...

  7. NULL, NUL, EOF, '\0',0区别

    NULL: 定义为0或0L或(void *)0,用于指示一个指针值是空,即什么都不指:'\0': 用且只用字符串结束符;NUL : 0x00,0值字符,可以用于结束ASCII字符串,和'\0'类似,但 ...

  8. FatMouse

      时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:1431 解决:641 题目描述: FatMouse prepared M pounds of cat food, ready to t ...

  9. Linux下crontab详解

    1.crond介绍 crond是Linux下的任务调度命令,让系统定期执行指定程序.crond命令每分钟都会检查是否有要执行的工作,若有要执行的程序便会自动执行. linux下任务调度工作主要分两类: ...

  10. easy ui datagrid 数据绑定

    1.前台页面 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...