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.google.common.base.Splitter; 019import com.jfinal.aop.Invocation; 020import com.jfinal.core.Action; 021import com.jfinal.core.ActionException; 022import com.jfinal.core.CPI; 023import com.jfinal.core.Controller; 024import com.jfinal.log.Log; 025import com.jfinal.render.RenderException; 026import io.jboot.components.valid.ValidException; 027import io.jboot.web.controller.JbootControllerContext; 028import io.jboot.web.session.JbootServletRequestWrapper; 029 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032import java.util.Collections; 033import java.util.Map; 034import java.util.TreeMap; 035 036/** 037 * @author 没牙的小朋友 (mjl@nxu.edu.cn) 038 * @version V1.0 039 */ 040public class PathVariableActionHandler extends JbootActionHandler { 041 042 private static final Log LOG = Log.getLog(PathVariableActionHandler.class); 043 044 /** 045 * handle 046 * 1: Action action = actionMapping.getAction(target) 047 * 2: new Invocation(...).invoke() 048 * 3: render(...) 049 */ 050 @Override 051 public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { 052 if (target.lastIndexOf('.') != -1) { 053 return; 054 } 055 056 isHandled[0] = true; 057 //urlPara数组增加第2个元素存储路径参数 058 String[] urlPara = {null, null}; 059 Action action = getAction(target, urlPara, request); 060 061 if (action == null) { 062 if (LOG.isWarnEnabled()) { 063 String qs = request.getQueryString(); 064 LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); 065 } 066 renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); 067 return; 068 } 069 070 071 Controller controller = null; 072 try { 073 controller = controllerFactory.getController(action.getControllerClass()); 074 //controller.init(request, response, urlPara[0]); 075 //存在封装的路径参数 076 if (urlPara[1] != null) { 077 Map<String, String> params = Splitter.on("&").withKeyValueSeparator("=").split(urlPara[1]); 078 PathVariableWrappedRequest wrappedRequest = new PathVariableWrappedRequest(request, response, params); 079 CPI._init_(controller, action, wrappedRequest, response, urlPara[0]); 080 } else { 081 CPI._init_(controller, action, request, response, urlPara[0]); 082 } 083 JbootControllerContext.hold(controller); 084 085 //Invocation invocation = new Invocation(action, controller); 086 Invocation invocation = getInvocation(action, controller); 087 088 if (JbootActionReporter.isReportEnable()) { 089 long time = System.currentTimeMillis(); 090 try { 091 doStartRender(target, action, controller, invocation, isHandled); 092 } finally { 093 JbootActionReporter.report(target, controller, action, invocation, time); 094 } 095 } else { 096 doStartRender(target, action, controller, invocation, isHandled); 097 } 098 099 doAfterRender(action, controller); 100 101 } catch (RenderException e) { 102 if (LOG.isErrorEnabled()) { 103 String qs = request.getQueryString(); 104 LOG.error(qs == null ? target : target + "?" + qs, e); 105 } 106 } catch (ActionException e) { 107 handleActionException(target, request, response, action, e); 108 } catch (ValidException e) { 109 handleValidException(target, request, response, action, e); 110 } catch (Exception e) { 111 handleException(target, request, response, action, e); 112 } finally { 113 JbootControllerContext.release(); 114 controllerFactory.recycle(controller); 115 } 116 } 117 118 119// private void doStartRender(String target 120// , HttpServletRequest request 121// , HttpServletResponse response 122// , boolean[] isHandled 123// , Action action 124// , Controller controller 125// , Invocation invocation) { 126// 127// invocation.invoke(); 128// 129// Render render = controller.getRender(); 130// if (render instanceof ForwardActionRender) { 131// String actionUrl = ((ForwardActionRender) render).getActionUrl(); 132// if (target.equals(actionUrl)) { 133// throw new RuntimeException("The forward action url is the same as before."); 134// } else { 135// handle(actionUrl, request, response, isHandled); 136// } 137// } else { 138// if (render == null && void.class != action.getMethod().getReturnType() 139// && renderManager.getRenderFactory() instanceof JbootRenderFactory) { 140// 141// JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); 142// JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); 143// String forwardTo = returnValueRender.getForwardTo(); 144// if (forwardTo != null) { 145// handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); 146// return; 147// } else { 148// render = returnValueRender; 149// } 150// } 151// 152// if (render == null) { 153// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); 154// } 155// 156// render.setContext(request, response, action.getViewPath()).render(); 157// } 158// } 159 160 /** 161 * 请求包装类用于将路径变量的URL中的额外参数加入request中 162 */ 163 private class PathVariableWrappedRequest extends JbootServletRequestWrapper { 164 private final Map<String, String[]> modifiableParameters; 165 private Map<String, String[]> allParameters = null; 166 167 public PathVariableWrappedRequest(HttpServletRequest request, HttpServletResponse response, 168 Map<String, String> params) { 169 super(request, response); 170 modifiableParameters = new TreeMap<>(); 171 params.keySet().forEach(k -> { 172 modifiableParameters.put(k, new String[]{params.get(k)}); 173 }); 174 } 175 176 @Override 177 public Map<String, String[]> getParameterMap() { 178 if (allParameters == null) { 179 allParameters = new TreeMap<String, String[]>(); 180 allParameters.putAll(super.getParameterMap()); 181 allParameters.putAll(modifiableParameters); 182 } 183 return Collections.unmodifiableMap(allParameters); 184 } 185 186 @Override 187 public String getParameter(final String name) { 188 String[] strings = getParameterMap().get(name); 189 if (strings != null) { 190 return strings[0]; 191 } 192 return super.getParameter(name); 193 } 194 } 195}