Spring MVC 与 web开发
转载: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开发的更多相关文章
- 基于Spring MVC的Web应用开发(三) - Resources
基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...
- Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用
前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...
- [翻译]Spring MVC RESTFul Web Service CRUD 例子
Spring MVC RESTFul Web Service CRUD 例子 本文主要翻译自:http://memorynotfound.com/spring-mvc-restful-web-serv ...
- spring mvc构建WEB应用程序入门例子
在使用spring mvc 构建web应用程序之前,需要了解spring mvc 的请求过程是怎样的,然后记录下如何搭建一个超简单的spring mvc例子. 1) spring mvc的请求经历 请 ...
- 使用IDEA和gradle搭建Spring MVC和MyBatis开发环境
1. 概述 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具. 它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐 ...
- Spring + Spring MVC + Hibernate项目开发集成(注解)
在自己从事的项目中都是使用xml配置的方式来进行的,随着项目的越来越大,会发现配置文件会相当的庞大,这个不利于项目的进行和后期的维护.于是考虑使用注解的方式来进行项目的开发,前些日子就抽空学习了一下. ...
- Spring基础系列-Web开发
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996902.html SpringBoot基础系列-web开发 概述 web开发就是集成 ...
- 译:7.使用Spring MVC服务Web内容
本指南向您介绍了使用Spring创建“hello world”网站的过程.阅读原文:Serving Web Content with Spring MVC 1. 你将会构建什么? 您将构建一个具有静态 ...
- 跟我学Spring Boot(三)Spring Boot 的web开发
1.Web开发中至关重要的一部分,Web开发的核心内容主要包括内嵌Servlet容器和SpringMVC spring boot 提供了spring-boot-starter-web 为web开发提 ...
随机推荐
- web项目嵌入Jetty运行的两种方式(Jetty插件和自制Jetty服务器)
在开发Java web项目时候,可以在项目中嵌入Jetty服务的方式来运行web程序. 由于最近开发web项目,自己使用的是比较旧的eclipse不支持导入tomcat来运行项目,于是就学习了下使用项 ...
- mysql 主从同步 Last_SQL_Error
参考文章: http://kerry.blog.51cto.com/172631/277414/ http://hancang2010.blog.163.com/blog/static/1824602 ...
- tomcat6.0添加ssi(*.shtml)配置
1.去掉tomcat6中conf/web.xml关于ssi的注释 <servlet> <servlet-name>ssi</servlet-name> <se ...
- 自己写的一个分页控件类(WinForm)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; u ...
- JSON C# Class Generator ---由json字符串生成C#实体类的工具
json作为互联网上轻量便捷的数据传输格式,越来越受到重视.但在服务器端编程过程中,我们常常希望能通过智能提示来提高编码效率.JSON C# Class Generator 能将json格式所表示的J ...
- 关于canvas中的jquery
关于h5,相比前端的同事们都很了解了吧!h5里面有个canvas,现在用的蛮火.但是canvas里面的代码确实是有点繁多,特别是要对于图形做什么操作的时候...我昨天无意间发现了一个canvas的插件 ...
- Android源代码编译——编译
环境准备好了,代码下载完了,据说make一下就可以. 当然也可以配置一下环境变量: 编译初始化,在终端中执行: source build/envsetup.sh 选择编译目标,在终端中执行下面的命令: ...
- STM8S003K3用Tim1的通道4输出20KHz的PWM波
void Init_pwm(){TIM1_DeInit(); TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, 800, 0x00);//250 TIM1_OC4In ...
- LR中错误代号为27796的解决方法
问题: 曾经遇到过一个问题,在一次性能测试过程中,使用http协议的多用户向服务器发送请求.设置了持续时间,出现错误为:27796, Failed to connect to server 'ho ...
- 【转】用perl写的单位电脑信息采集程序
perl,后来我又改过了增加了一些交互和数据库检测的功能.主要用于收集ip.mac.姓名.房间,后来又加入了维修记录的功能.服务器端接受数据并存入数据库中. 代码如下: 主要用于收集ip.mac.姓名 ...