001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.web.handler; 017 018import com.jfinal.aop.Invocation; 019import com.jfinal.core.*; 020import com.jfinal.log.Log; 021import com.jfinal.render.IRenderFactory; 022import com.jfinal.render.Render; 023import com.jfinal.render.RenderException; 024import com.jfinal.template.TemplateException; 025import io.jboot.app.JbootApplicationConfig; 026import io.jboot.components.valid.ValidErrorRender; 027import io.jboot.components.valid.ValidException; 028import io.jboot.components.valid.ValidUtil; 029import io.jboot.utils.ClassUtil; 030import io.jboot.web.controller.JbootControllerContext; 031import io.jboot.web.render.JbootErrorRender; 032import io.jboot.web.render.JbootRenderFactory; 033import io.jboot.web.render.JbootReturnValueRender; 034 035import javax.servlet.http.HttpServletRequest; 036import javax.servlet.http.HttpServletResponse; 037 038/** 039 * @author Michael Yang 杨福海 (fuhai999@gmail.com) 040 * @version V1.0 041 */ 042public class JbootActionHandler extends ActionHandler { 043 044 045 private static final Log LOG = Log.getLog(JbootActionHandler.class); 046 private static final JbootApplicationConfig appConfig = JbootApplicationConfig.get(); 047 048 /** 049 * 方便子类复写、从而可以实现 自定义 Action 的功能 050 * 051 * @param target 052 * @param urlPara 053 * @param request 054 * @return 055 */ 056 public Action getAction(String target, String[] urlPara, HttpServletRequest request) { 057 return this.getAction(target, urlPara); 058 } 059 060 061 /** 062 * 方便子类复写、从而可以实现 自定义 Action 的功能 063 * 064 * @param target 065 * @param urlPara 066 * @return 067 */ 068 @Override 069 protected Action getAction(String target, String[] urlPara) { 070 return super.getAction(target, urlPara); 071 } 072 073 /** 074 * 方便子类复写、从而可以实现 自定义 Invocation 的功能 075 * 076 * @param action 077 * @param controller 078 * @return 079 */ 080 public Invocation getInvocation(Action action, Controller controller) { 081 return JbootActionReporter.isReportEnable() ? new JbootActionReporterInvocation(action, controller) : new JbootActionInvocation(action, controller); 082 } 083 084 085 /** 086 * handle 087 * 1: Action action = actionMapping.getAction(target) 088 * 2: new Invocation(...).invoke() 089 * 3: render(...) 090 */ 091 @Override 092 public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { 093 if (target.lastIndexOf('.') != -1) { 094 return; 095 } 096 097 isHandled[0] = true; 098 String[] urlPara = {null}; 099 Action action = getAction(target, urlPara, request); 100 101 if (action == null) { 102 if (!appConfig.isHandle404()) { 103 isHandled[0] = false; 104 return; 105 } 106 107 if (LOG.isWarnEnabled()) { 108 String qs = request.getQueryString(); 109 LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); 110 } 111 renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); 112 return; 113 } 114 Controller controller = null; 115 try { 116 controller = controllerFactory.getController(action.getControllerClass()); 117 //controller.init(request, response, urlPara[0]); 118 CPI._init_(controller, action, request, response, urlPara[0]); 119 120 JbootControllerContext.hold(controller); 121 122 //Invocation invocation = new Invocation(action, controller); 123 Invocation invocation = getInvocation(action, controller); 124 125 if (JbootActionReporter.isReportEnable()) { 126 long time = System.currentTimeMillis(); 127 try { 128 doStartRender(target, action, controller, invocation, isHandled); 129 } finally { 130 JbootActionReporter.report(target, controller, action, invocation, time); 131 } 132 } else { 133 doStartRender(target, action, controller, invocation, isHandled); 134 } 135 136 doAfterRender(action, controller); 137 138 } catch (RenderException e) { 139 if (LOG.isErrorEnabled()) { 140 String qs = request.getQueryString(); 141 LOG.error(qs == null ? target : target + "?" + qs, e); 142 } 143 } catch (ActionException e) { 144 if (e.getErrorCode() == 404 && !appConfig.isHandle404()) { 145 isHandled[0] = false; 146 return; 147 } 148 handleActionException(target, request, response, action, e); 149 } catch (ValidException e) { 150 handleValidException(target, request, response, action, e); 151 } catch (TemplateException e) { 152 handleTemplateException(target, request, response, action, e); 153 } catch (Exception e) { 154 handleException(target, request, response, action, e); 155 } finally { 156 JbootControllerContext.release(); 157 controllerFactory.recycle(controller); 158 } 159 } 160 161 protected boolean isJspTarget(String target) { 162 return target.toLowerCase().contains(".jsp"); 163 } 164 165 166 protected void doAfterRender(Action action, Controller controller) { 167 } 168 169 170 protected void doStartRender(String target 171 , Action action 172 , Controller controller 173 , Invocation invocation 174 , boolean[] isHandled) { 175 176 invocation.invoke(); 177 178 Render render = controller.getRender(); 179 if (render instanceof ForwardActionRender) { 180 String actionUrl = ((ForwardActionRender) render).getActionUrl(); 181 if (target.equals(actionUrl)) { 182 throw new RuntimeException("The forward action url is the same as before."); 183 } else { 184 handle(actionUrl, controller.getRequest(), controller.getResponse(), isHandled); 185 } 186 } else { 187 if (render == null && void.class != action.getMethod().getReturnType() 188 && renderManager.getRenderFactory() instanceof JbootRenderFactory) { 189 190 JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); 191 JbootReturnValueRender returnValueRender = factory.getReturnValueRender(invocation.getReturnValue()); 192 193 String forwardTo = returnValueRender.getForwardTo(); 194 if (forwardTo != null) { 195 handle(getRealForwrdTo(forwardTo, target, action), controller.getRequest(), controller.getResponse(), isHandled); 196 return; 197 } else { 198 render = returnValueRender; 199 //重新设置到 Controller,JbootActionReporter 才能 Controller 获取 render 判断 render 类型 200 controller.render(render); 201 } 202 } 203 204 if (render == null) { 205 render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); 206 207 //重新设置到 Controller,JbootActionReporter 才能 Controller 获取 render 判断 render 类型 208 controller.render(render); 209 } 210 211 render.setContext(controller.getRequest(), controller.getResponse(), action.getViewPath()).render(); 212 } 213 } 214 215 public String getRealForwrdTo(String forwardTo, String currentTarget, Action action) { 216 if ("".equals(forwardTo)) { 217 throw new IllegalArgumentException(ClassUtil.buildMethodString(action.getMethod()) + ": The forward key can not be blank."); 218 } 219 220 if (forwardTo.startsWith("/")) { 221 return forwardTo; 222 } 223 224 225 if (forwardTo.startsWith("./")) { 226 return currentTarget.substring(0, currentTarget.lastIndexOf("/")) + forwardTo.substring(1); 227 } 228 229 return "/" + forwardTo; 230 } 231 232 233 /** 234 * 处理错误信息 235 * 236 * @param target 237 * @param request 238 * @param response 239 * @param action 240 * @param e 241 */ 242 protected void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) { 243 int errorCode = e.getErrorCode(); 244 String msg = null; 245 if (errorCode == 404) { 246 msg = "404 Not Found: "; 247 } else if (errorCode == 400) { 248 msg = "400 Bad Request: "; 249 } else if (errorCode == 401) { 250 msg = "401 Unauthorized: "; 251 } else if (errorCode == 403) { 252 msg = "403 Forbidden: "; 253 } 254 255 256 if (msg != null) { 257 if (errorCode == 404 || errorCode == 401 || errorCode == 403) { 258 if (LOG.isWarnEnabled()) { 259 String qs = request.getQueryString(); 260 msg = msg + (qs == null ? target : target + "?" + qs); 261 LOG.info(msg, e); 262 } 263 } else { 264 if (LOG.isErrorEnabled()) { 265 String qs = request.getQueryString(); 266 msg = msg + (qs == null ? target : target + "?" + qs); 267 LOG.error(msg, e); 268 } 269 } 270 } else { 271 if (LOG.isErrorEnabled()) { 272 String qs = request.getQueryString(); 273 LOG.error(errorCode + " Error: " + (qs == null ? target : target + "?" + qs), e); 274 } 275 } 276 277 e.getErrorRender().setContext(request, response, action.getViewPath()).render(); 278 } 279 280 281 /** 282 * 处理参数验证错误 283 */ 284 protected void handleValidException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ValidException validException) { 285 if (LOG.isErrorEnabled()) { 286// String qs = request.getQueryString(); 287// String targetInfo = qs == null ? target : target + "?" + qs; 288// LOG.error(validException.getReason() + " : " + targetInfo, validException); 289 LOG.error("Invalid parameter: " + validException.getReason()); 290 } 291 IRenderFactory factory = renderManager.getRenderFactory(); 292 if (factory instanceof JbootRenderFactory) { 293 ValidErrorRender render = ((JbootRenderFactory) factory).getValidErrorRender(validException); 294 render.setContext(request, response, action.getViewPath()).render(); 295 } else { 296 Render render = renderManager.getRenderFactory().getErrorRender(ValidUtil.getErrorCode()); 297 if (render instanceof JbootErrorRender) { 298 ((JbootErrorRender) render).setThrowable(validException); 299 } 300 render.setContext(request, response, action.getViewPath()).render(); 301 } 302 } 303 304 305 /** 306 * 处理模板错误 307 */ 308 protected void handleTemplateException(String target, HttpServletRequest request, HttpServletResponse response, Action action, TemplateException e) { 309 String qs = request.getQueryString(); 310 String targetInfo = qs == null ? target : target + "?" + qs; 311 String info = ClassUtil.buildMethodString(action.getMethod()); 312 LOG.error(info + " \nQuery: " + targetInfo + "\n", e); 313 314 IRenderFactory factory = renderManager.getRenderFactory(); 315 if (factory instanceof JbootRenderFactory) { 316 ((JbootRenderFactory) factory).getTemplateErrorRender(e).setContext(request, response, action.getViewPath()).render(); 317 } 318 } 319 320 321 /** 322 * 处理其他业务错误 323 */ 324 protected void handleException(String target, HttpServletRequest request, HttpServletResponse response, Action action, Exception e) { 325 String qs = request.getQueryString(); 326 String targetInfo = qs == null ? target : target + "?" + qs; 327 String info = ClassUtil.buildMethodString(action.getMethod()); 328 LOG.error(info + " \nQuery: " + targetInfo + "\n", e); 329 renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render(); 330 } 331 332 333}