Jfinal validator 拿不到Post请求中body里的数据,验证失败

最近用jfinal 开发项目,其中有个接口请求方式为post,但因为请求参数比较多,所以就写了个继承自Validator 的类,放在了接口前面。问题是,我的请求参数都正确,却校验不通过。通过跟踪源码发现, Validator 类中,获取参数的方式是controller.getPara(field),这个接口是post 请求,参数在body 中,于是无法取到参数,校验不通过。

于是我想到封装一下Validator 这个类,但是由于 Validator 类中,校验的方法都是采用Controller.getPara()方式,一个个改,虽然可以但是工作量比较大,所以,有没有哪位大神提供一个比较好的解决方案?

评论区

JFinal

2017-10-19 17:06

这种情况下,参数是通过什么方式获取的? 是不是通过
HttpKit.readData(getRequest()) 获取到的?

JennyJingXiao

2017-10-19 17:07

JFinal

2017-10-19 17:09

@JennyJingXiao Validator 本质就是一个拦截器,你可以参考 Validator 中的代码改造为一个自己的 PostBodyValidator 出来,然后定制自己的 validateXxx 方法

JFinal

2017-10-19 17:09

jfinal 默认的 Validator 是为 form 表单这类参数准备的

JennyJingXiao

2017-10-19 17:12

@JFinal 本来我想通过采用设计模式来修改一下Validator 中的校验函数,但是似乎行不通,现在也只能自己实现一个PostBodyValidator 了

JFinal

2017-10-19 17:13

刚想到了一个更简单的办法:
1:Validator 中的 controller 属性是 protected 的,你可以通过定制一个自己的 MyValidator ,将父类的 controller 换成自己的 Controller 实现类 MockController

2:做一个 MockController 继承一下 Controller ,并持有当前请求的 controller 对象

3:在 MockController 中覆盖掉父类的 getPara(...) 这类方法,接管当前正在请求的真正的 controller 对象的行为

4:getPara() 接管之前,将当前真正的 controller 对象内的属性获取到,并且解析成为可以被 validate 的形式

记得搞定后回来分享

JennyJingXiao

2017-10-19 17:13

@JFinal 好的,谢谢,反馈很给力

JennyJingXiao

2017-10-19 18:05

@JFinal 之前你说的方案可以行得通。其实,不用MockController,直接在请求方法的Controller 中重写 getPara()方法就可以了。但是需要注意,可能需要实现getpara(field) 和 getpara(index) 这个两个方法,还是要看你用了方法调用了哪个getpara()

JennyJingXiao

2017-10-19 18:07

@JFinal 关于controller 中getpara() 重写的实现,我贴一下我的代码,如果有更好的方案,请与大家分享

JennyJingXiao

2017-10-19 18:07

@Override
public String getPara(String name) {
if (REQUEST_METHOD_POST.equals(getRequest().getMethod())) {
if (null == postParamMap || postParamMap.isEmpty()) {
try {
postParamMap = JacksonUtils.Json2Map(HttpKit
.readData(getRequest()));
} catch (Exception e) {
return "";
}
}
Object result = postParamMap.get(name);
postParamMap.remove(name);
return (String) result;
} else {
return super.getPara(name);
}
}

private static Map postParamMap = null;

private final static String REQUEST_METHOD_POST = "POST";
private final static String REQUEST_METHOD_GET = "GET";

JennyJingXiao

2017-10-19 18:09

public class MyValidator extends Validator{


@Override
protected void validate(Controller c) {
validateRequired("name", "nameErrorMsg", "姓名不能为空");
validateRequired("gender", "genderErrorMsg", "性别不能为空");

}

@Override
protected void handleError(Controller c) {
c.keepPara(new String []{"name","age"});
c.renderText("校验失败");
}

}

JFinal

2017-10-19 18:50

这种重写的方式非常好,注意一个重要问题, Map postParamMap 这个属性去掉 static 关键字,否则有线程安全问题

此外,可以引入一个中间控制器继承一下 Controller,例如叫: PostBodyController,在这个中间控制器中覆盖掉 getPara(...) 方法,那么在以后碰到这类传参方式可以让你的最终 MyController 继承 PostBodyController,这样就可以重用代码了

大致如下:
public class PostBodyController extends Controller {
// 覆盖掉父类的 getPara 实现
public String getPara(...) {
....
}
}

最终的控制器如 UserController 如下:
public class UserController extends PostBodyController {
// 这里可使用普通的 validator
@Before(UserValidator.class)
public void index() {
...
}
}

JennyJingXiao

2017-10-19 20:51

@JFinal 哦,线程安全问题给忽略了。引入中间控制器的方式后来也想到了,这种方式比较优雅

战儒小子

2017-11-30 10:23

@JennyJingXiao 重写getPara时调用了HttpKit.readData(getRequest()),会导致BufferedReader关闭,在方法中再次调用HttpKit.readData(getRequest())时,会提示Stream closed,不知道这个问题楼主怎么解决的?

tomato007

2018-11-01 17:55

@战儒小子 /**
* 获取 http 请求 body 中的原始数据,通常用于接收 json String 这类数据

* 可多次调用此方法,避免掉了 HttpKit.readData(...) 方式获取该数据时多次调用
* 引发的异常
* @return http 请求 body 中的原始数据
*/
public String getRawData() {
if (rawData == null) {
rawData = com.jfinal.kit.HttpKit.readData(request);
}
return rawData;
}

tomato007

2018-11-01 17:57

@战儒小子 使用getRawData方法

小辉大大

2019-06-11 12:34

@JennyJingXiao JacksonUtils.Json2Map是什么方法,貌似com.github.fge.jackson.JacksonUtils下没有Json2Map这个方法。

JFinal

2019-06-11 12:43

@小辉大大 用 Map map = FastJson.getJson().parse(jsonString, Map.class) 就好

热门反馈

扫码入社