如题,为了返回格式的统一化,尤其是当出现异常时,我做了如下的尝试:
首先建立全局异常拦截器:GlobalExceptionInterger
public class JfastGlobeExceptionInterceptor implements Interceptor{
@Override
public void intercept(Invocation inv) {
try{
inv.invoke();
}catch (Exception e) {
e.printStackTrace();
String errName = e.getClass().getName();
if(errName.endsWith("NotAuthException")){
//ErrRender 是我自定义的一个类,用来识别是否ajax请求和获取网页要求的返回格式是否为json
ErrRender.auto(inv.getController(), 1000, e.getMessage());
return;
}else if(errName.endsWith("ActionException")){
//???? renderError抛出的异常
return;
}else{
ErrRender.auto(inv.getController(), 1001, e.getMessage());
return;
}
}
}问题来了:Jfinal中有一类renderError,它的本质也是抛出异常,当然会被捕捉到。貌似Jfinal正常情况下,运行时发生的异常都会通过它来抛出。我该如何处理呢?
思路1、我在拦截器里对数据进行判断,再进行相应的返回。复杂!这里有我自己在控制器中执行的,还有Jfinal产生的。更深层的是:
renderError只有两个重载方法renderError(int errCode)和renderError(int errCode, String view)。这意味着,我在控制器中不能自定义文字、说明错误(或者在view 中写,但这实现起来也不容易吧!)。
结论:不便在拦截器中对这类异常进一步处理。
思路2、对renderError及相关的类进行修改,令其自行处理返回内容的格式。可是发现不能传入自定义的信息。
首先,我建立了MyErrorRender并继承ErrorRender。
public class MyErrorRender extends ErrorRender {
protected String msg ;
public MyErrorRender(int errorCode, String view) {
super(errorCode, view);
}
/**
* JFinal 的Controller限制了发挥,下面的构造方法用不上!
* <br>期盼以后可以用上吧
* @param msg
* @param errorCode
*/
public MyErrorRender(String msg, int errorCode) {
super(errorCode, null);
this.msg = msg;
}
@Override
public void render() {
response.setStatus(getErrorCode()); // HttpServletResponse.SC_XXX_XXX
PrintWriter writer = null;
try {
if(XX.isAjax(request) && XX.isJsonRender(request)){
RenderManager.me().getRenderFactory().getJsonRender(getErrorJson()).setContext(request, response).render();;
return;
}
// render with view
String view = getView();
if (view != null) {
RenderManager.me().getRenderFactory().getRender(view).setContext(request, response).render();
return;
}
// render with html content
response.setContentType(contentType);
writer = response.getWriter();
writer.write(getErrorHtml());
writer.flush();
} catch (IOException e) {
throw new RenderException(e);
}
}
/**
* 获得返回的json格式数据
* @author WangWei
* @created 2017-10-19 下午2:28:16
* @return
*/
private String getErrorJson() {
int errorCode = super.getErrorCode();
msg = this.msg == null?errorCode + " 系统错误":this.msg;
return JsonKit.toJson(Kv.by("errCode", errorCode).set("msg", msg));
}
/**
* 尝试通过过滤HTTP状态码,进行自定义错误输出,以便拦截器处理
*/
public int getErrorCode(){
int myCode = super.getErrorCode();
ArrayList<Integer> sysCode = new ArrayList<>(Arrays.asList(201,202,203,204,
301,302,303,304,305,306,
400,401,402,403,404,407,415,
500,501,502,503));
if(sysCode.contains(myCode)){
return myCode;
}else{
return 200;
}
}
}按照教程设置了MyRenderFactory extends RenderFactory,并进行了配置。
然而,似乎上述工作是徒劳的。因为,renderError是要抛出异常的,必然还是被全局异常拦截器拦截。这就又回到思路1中。
那么,只能退而求其次,在errCode和view上下功夫了:即通过errCode将错误分类。然后在全局异常拦截器中直接将ActionException异常抛出,而不做其他任何处理。
现在,就剩下一个问题没有解决了:如何传入自定义的错误信息。
控制器中可以setAttr("errMsg","错误信息"),然后在MyErrorRender类中通过request.getAttribute("errMsg").toString()获取。
不知道有没有更好的解决方案?
附:我对json格式返回统一为:{“errCode”: 1001,"msg":"自定义的错误信息"}
2017.10.24更新
经过进一步思考,基本实现了“智能”地统一返回。基本思路还是用了上述的“思路1”(本以为复杂,但深入思考后化繁为简了!)
对全局异常拦截优化如下:
public class JfastGlobeExceptionInterceptor implements Interceptor{
@Override
public void intercept(Invocation inv) {
try{
inv.invoke();
}catch (Exception e) {
e.printStackTrace();
String errName = e.getClass().getName();
if(errName.endsWith("NotAuthException")){
String errMsg = e.getMessage();
inv.getController().setAttr("errMsg", StrKit.isBlank(errMsg)?"系统错误":errMsg);
inv.getController().renderError(1000);
return;
}else if(errName.endsWith("ActionException")){
// renderError抛出的异常
throw e;
}else{
String errMsg = e.getMessage();
inv.getController().setAttr("errMsg", StrKit.isBlank(errMsg)?"系统错误":errMsg);
inv.getController().renderError(2);
return;
}
}
}并将MyErrorRender改为继承Render,而不再继承ErrorRender,以便深度扩展,具体如下:
package com.jfast.core.config;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import com.jfast.util.XX;
import com.jfinal.config.Constants;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;
import com.jfinal.render.RenderManager;
import com.jfinal.template.Engine;
public class MyErrorRender extends Render {
protected static final String contentType = "text/html; charset=" + getEncoding();
protected static final String version = "<center><a href='http://www.jfinal.com?f=ev-" + Const.JFINAL_VERSION + "' target='_blank'><b>Powered by JFinal " + Const.JFINAL_VERSION + "</b></a></center>";
protected static final String html404 = "<html><head><title>404 Not Found</title></head><body bgcolor='white'><center><h1>404 Not Found</h1></center><hr>" + version + "</body></html>";
protected static final String html500 = "<html><head><title>500 Internal Server Error</title></head><body bgcolor='white'><center><h1>500 Internal Server Error</h1></center><hr>" + version + "</body></html>";
protected static final String html401 = "<html><head><title>401 Unauthorized</title></head><body bgcolor='white'><center><h1>401 Unauthorized</h1></center><hr>" + version + "</body></html>";
protected static final String html403 = "<html><head><title>403 Forbidden</title></head><body bgcolor='white'><center><h1>403 Forbidden</h1></center><hr>" + version + "</body></html>";
protected String msg ;
protected int errorCode;
public MyErrorRender(){
}
public MyErrorRender(int errorCode){
this.errorCode = errorCode;
}
public MyErrorRender(int errorCode, String view) {
this.errorCode = errorCode;
this.view = view;
}
/**
* JFinal 的Controller限制了发挥,不能直接使用下面的构造方法
* <br>可以在控制其中这样使用:
* <br>this.render(new MyErrorRender(msg, errorCode));
* @param msg
* @param errorCode
*/
public MyErrorRender(String msg, int errorCode) {
this.errorCode = errorCode;
this.msg = msg;
}
@Override
public void render() {
response.setStatus(getErrorCode()); // HttpServletResponse.SC_XXX_XXX
PrintWriter writer = null;
try {
// XX 是我自定义的一个工具类,进行一些判断操作
if(XX.isAjax(request) && XX.isJsonRender(request)){
RenderManager.me().getRenderFactory().getJsonRender(getErrorJson()).setContext(request, response).render();;
return;
}
// render with view
String view = getView();
if (view != null) {
RenderManager.me().getRenderFactory().getRender(view).setContext(request, response).render();
return;
}
// render with html content
response.setContentType(contentType);
String template = getErrorHtml();
Kv k = new Kv();
if(StrKit.isBlank(msg)){
k= Kv.by("errMsg", request.getAttribute("errMsg"));
}else{
k = Kv.by("errMsg", msg);
}
template = Engine.use().getTemplateByString(template).renderToString(k);
writer = response.getWriter();
writer.write(template);
writer.flush();
} catch (IOException e) {
throw new RenderException(e);
}
}
/**
* 获得返回的json格式数据
* @author WangWei
* @created 2017-10-19 下午2:28:16
* @return
*/
private String getErrorJson() {
int errCode = this.errorCode;
Object tempMsg = request.getAttribute("errMsg");
String errMsg = null;
if(!XX.isEmpty(tempMsg)){
errMsg = tempMsg.toString();
}
msg =errCode + " " + (this.msg == null ? ( null == errMsg ? "系统错误": errMsg):this.msg);
return JsonKit.toJson(Kv.by("errCode", errCode).set("msg", msg));
}
public int getErrorCode(){
int myCode = this.errorCode;
ArrayList<Integer> sysCode = new ArrayList<>(Arrays.asList(201,202,203,204,
301,302,303,304,305,306,
400,401,402,403,404,407,415,
500,501,502,503));
if(sysCode.contains(myCode)){
return myCode;
}else{
return 200;
}
}
public String getErrorHtml() {
int errorCode = this.errorCode;
String html = new Constants().getErrorView(errorCode);
if(StrKit.isBlank(html)){
if (errorCode == 404)
return html404;
if (errorCode == 500)
return html500;
if (errorCode == 401)
return html401;
if (errorCode == 403)
return html403;
}else{
return html;
}
return "<html><head><title>" + errorCode + " #(errMsg??'Error')</title></head><body bgcolor='white'><center><h1>" + errorCode + " #(errMsg??'Error')</h1></center><hr><center><a href='http://www.51xlxy.com?v="+JfastConst.VERSION+"' target='_blank'><b>Powered by JFast "+JfastConst.VERSION+"</b></a></center></body></html>";
}
}使用方法:
1、系统配置
在Jfinal的配置类中设置:
public void configConstant(Constants me) {
……
me.setRenderFactory(new MyRenderFactory());
}2、MyRenderFactory
public class MyRenderFactory extends RenderFactory{
@Override
public Render getErrorRender(int errCode){
return new MyErrorRender(errCode,constants.getErrorView(errCode));
}
@Override
public Render getErrorRender(int errCode, String view){
return new MyErrorRender(errCode,view);
}
//
}3、使用
在控制器中需要返回错误或者异常的地方:
this.renderError(errCode); this.renderError(errCode, view);//或者这样。 /* * this.renderError(errCode, errRender); * 这个用法要小心: * errRender变量的类型最好还是自定义的MyErrorRender; * 如果是其他类型,返回值就跳出了我们辛苦设置的范围。 */ this.renderError(errCode, errRender); //上述方式其实是要抛出异常的,不用写return; //下面的方式,不会主动抛出异常: this.render(new MyErrorRender(errCode)); this.render(new MyErrorRender(errCode, view)); this.render(new MyErrorRender(errMsg, errCode));
写到这里,我就有点小激动,因为,我“朝思暮想”的自定义错误信息,可以通过上述最后一个方法实现了!
方案缺陷:
未能实现Controller.renderError(errCode, errRender)这一方法的自定义。
因此,如果我们或者Jfinal系统内部使用了这一方法,就需要web前端开发中考虑这一特殊情形。