这里的nextRequestResponse是指RequestHandler中doRequest()函数在最后使用的一个变量,doRequest()会依据nextRequestResponse返回不同的响应给请求者。nextRequestResponse有多种不同的类型,不同的类型处理方式也不一样。
第一种类型是request,表明这是一个请求链,递归调用doRequest()处理下一个请求:
// <request-map uri="main"> // <response name="success" type="request" value="other"/> // </request-map> if ("request".equals(nextRequestResponse.type)) { // chained request doRequest(request, response, nextRequestResponse.value, userLogin, delegator); }
处理nextRequestResponse时,除了request类型,其它类型都需要执行Postprocessor。Postprocessor不会终止请求过程,如果返回的结果不是success,则以抛出异常的方式处理:
for (ConfigXMLReader.Event event: controllerConfig.getPostprocessorEventList().values()) { try { String returnString = this.runEvent(request, response, event, requestMap, "postprocessor"); if (returnString != null && !returnString.equalsIgnoreCase("success")) { throw new EventHandlerException("Post-Processor event did not return 'success'."); } } catch (EventHandlerException e) { Debug.logError(e, module); } }
第二种类型是url,表明这是一个URL重定向:
// <request-map uri="main"> // <response name="success" type="url" value="http://www.baidu.com"/> // </request-map> if ("url".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect.", module); callRedirect(nextRequestResponse.value, response, request); }
callRedirect()做了两件事,一是将request中可序列化的对象保存到session(这样处理的原因是什么?),二是调用response的sendResponse()执行重定向:
private void callRedirect(String url, HttpServletResponse resp, HttpServletRequest req) throws RequestHandlerException { if (Debug.infoOn()) Debug.logInfo("Sending redirect to: [" + url + "], sessionId=" + UtilHttp.getSessionId(req), module); // set the attributes in the session so we can access it. // 将request中可序列化的属性保存到session Enumeration<String> attributeNameEnum = UtilGenerics.cast(req.getAttributeNames()); Map<String, Object> reqAttrMap = FastMap.newInstance(); while (attributeNameEnum.hasMoreElements()) { String name = attributeNameEnum.nextElement(); Object obj = req.getAttribute(name); if (obj instanceof Serializable) { reqAttrMap.put(name, obj); } } if (reqAttrMap.size() > 0) { // RequestHandler is not serializable and must be removed first. reqAttrMap.remove("_REQUEST_HANDLER_"); byte[] reqAttrMapBytes = UtilObject.getBytes(reqAttrMap); if (reqAttrMapBytes != null) { req.getSession().setAttribute("_REQ_ATTR_MAP_", StringUtil.toHexString(reqAttrMapBytes)); } } // send the redirect // 响应重定向 try { resp.sendRedirect(url); } catch (IOException ioe) { throw new RequestHandlerException(ioe.getMessage(), ioe); } catch (IllegalStateException ise) { throw new RequestHandlerException(ise.getMessage(), ise); } }
第三种类型是cross-request,表明这是一个跨应用(Cross Application)重定向。什么是跨应用重定向?例如,如果当前请求的是http://localhost:8080/practice/control/main,跨应用重定向后的地址就是http://localhost:8080/other。这里的practice就是一个应用,other是另外一个应用。
// <request-map uri="main"> // <response name="success" type="cross-request" value="other"/> // </request-map> if ("cross-redirect".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Cross-Application redirect.", module); String url = nextRequestResponse.value.startsWith("/") ? nextRequestResponse.value : "/" + nextRequestResponse.value; callRedirect(url + this.makeQueryString(request, nextRequestResponse), response, request); }
makeQueryString()处理重定向后的请求参数。请求参数有两个来源,一是当前request对象的QueryString,二是nextRequestResponse的redirect-parameter。
public String makeQueryString(HttpServletRequest request, ConfigXMLReader.RequestResponse requestResponse) { if (requestResponse == null || (requestResponse.redirectParameterMap.size() == 0 && requestResponse.redirectParameterValueMap.size() == 0)) { Map<String, Object> urlParams = UtilHttp.getUrlOnlyParameterMap(request); String queryString = UtilHttp.urlEncodeArgs(urlParams, false); if(UtilValidate.isEmpty(queryString)) { return queryString; } return "?" + queryString; } else { // redirect-parameter可以使用value指定具体的参数值, 也可以使用from指定参数值的来源。例如: // <request-map uri="main"> // <response name="success" type="cross-request" value="other"> // <request-parameter name="foo" value="xxx"/> // <request-parameter name="bar" from="jsessionid"/> // </response> // </request-map> StringBuilder queryString = new StringBuilder(); queryString.append("?"); for (Map.Entry<String, String> entry: requestResponse.redirectParameterMap.entrySet()) { String name = entry.getKey(); String from = entry.getValue(); Object value = request.getAttribute(from); if (value == null) { value = request.getParameter(from); } addNameValuePairToQueryString(queryString, name, (String) value); } for (Map.Entry<String, String> entry: requestResponse.redirectParameterValueMap.entrySet()) { String name = entry.getKey(); String value = entry.getValue(); addNameValuePairToQueryString(queryString, name, (String) value); } return queryString.toString(); } }
第四种类型是request-redirect,表明这是一个请求重定向。什么是请求重定向?例如,如果当前请求的是http://localhost:8080/practice/control/main,请求重定向后的地址就是http://localhost:8080/practice/control/other,都是在同一个应用practice里面。
// <request-map uri="main"> // <response name="success" type="request-request" value="other"/> // </request-map> if ("request-redirect".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect.", module); callRedirect(makeLinkWithQueryString(request, response, "/" + nextRequestResponse.value, nextRequestResponse), response, request); }
makeLinkWithQueryString()是在makeQueryString()基础上增加了makeLink()的调用:
public String makeLinkWithQueryString(HttpServletRequest request, HttpServletResponse response, String url, ConfigXMLReader.RequestResponse requestResponse) { String initialLink = this.makeLink(request, response, url); String queryString = this.makeQueryString(request, requestResponse); return initialLink + queryString; } public String makeLink(HttpServletRequest request, HttpServletResponse response, String url) { return makeLink(request, response, url, false, false, true); } public String makeLink(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode) { Delegator delegator = (Delegator) request.getAttribute("delegator"); String webSiteId = WebSiteWorker.getWebSiteId(request); String httpsPort = null; String httpsServer = null; String httpPort = null; String httpServer = null; Boolean enableHttps = null; // load the properties from the website entity GenericValue webSite; if (webSiteId != null) { try { webSite = delegator.findByPrimaryKeyCache("WebSite", UtilMisc.toMap("webSiteId", webSiteId)); if (webSite != null) { httpsPort = webSite.getString("httpsPort"); httpsServer = webSite.getString("httpsHost"); httpPort = webSite.getString("httpPort"); httpServer = webSite.getString("httpHost"); enableHttps = webSite.getBoolean("enableHttps"); } } catch (GenericEntityException e) { Debug.logWarning(e, "Problems with WebSite entity; using global defaults", module); } } // fill in any missing properties with fields from the global file if (UtilValidate.isEmpty(httpsPort)) { httpsPort = UtilProperties.getPropertyValue("url.properties", "port.https", "443"); } if (UtilValidate.isEmpty(httpsServer)) { httpsServer = UtilProperties.getPropertyValue("url.properties", "force.https.host"); } if (UtilValidate.isEmpty(httpPort)) { httpPort = UtilProperties.getPropertyValue("url.properties", "port.http", "80"); } if (UtilValidate.isEmpty(httpServer)) { httpServer = UtilProperties.getPropertyValue("url.properties", "force.http.host"); } if (enableHttps == null) { enableHttps = UtilProperties.propertyValueEqualsIgnoreCase("url.properties", "port.https.enabled", "Y"); } // create the path the the control servlet String controlPath = (String) request.getAttribute("_CONTROL_PATH_"); String requestUri = RequestHandler.getRequestUri(url); ConfigXMLReader.RequestMap requestMap = null; if (requestUri != null) { requestMap = getControllerConfig().getRequestMapMap().get(requestUri); } StringBuilder newURL = new StringBuilder(); boolean didFullSecure = false; boolean didFullStandard = false; if (requestMap != null && (enableHttps || fullPath || secure)) { if (Debug.verboseOn()) Debug.logVerbose("In makeLink requestUri=" + requestUri, module); if (secure || (enableHttps && requestMap.securityHttps && !request.isSecure())) { String server = httpsServer; if (UtilValidate.isEmpty(server)) { server = request.getServerName(); } newURL.append("https://"); newURL.append(server); if (!httpsPort.equals("443")) { newURL.append(":").append(httpsPort); } didFullSecure = true; } else if (fullPath || (enableHttps && !requestMap.securityHttps && request.isSecure())) { String server = httpServer; if (UtilValidate.isEmpty(server)) { server = request.getServerName(); } newURL.append("http://"); newURL.append(server); if (!httpPort.equals("80")) { newURL.append(":").append(httpPort); } didFullStandard = true; } } newURL.append(controlPath); // now add the actual passed url, but if it doesn't start with a / add one first if (!url.startsWith("/")) { newURL.append("/"); } newURL.append(url); String encodedUrl; if (encode) { boolean forceManualJsessionid = "false".equals(getServletContext().getInitParameter("cookies")) ? true : false; boolean isSpider = false; // if the current request comes from a spider, we will not add the jsessionid to the link if (UtilHttp.checkURLforSpiders(request)) { isSpider = true; } // if this isn't a secure page, but we made a secure URL, make sure we manually add the jsessionid since the response.encodeURL won't do that if (!request.isSecure() && didFullSecure) { forceManualJsessionid = true; } // if this is a secure page, but we made a standard URL, make sure we manually add the jsessionid since the response.encodeURL won't do that if (request.isSecure() && didFullStandard) { forceManualJsessionid = true; } if (response != null && !forceManualJsessionid && !isSpider) { encodedUrl = response.encodeURL(newURL.toString()); } else { if (!isSpider) { String sessionId = ";jsessionid=" + request.getSession().getId(); // this should be inserted just after the "?" for the parameters, if there is one, or at the end of the string int questionIndex = newURL.indexOf("?"); if (questionIndex == -1) { newURL.append(sessionId); } else { newURL.insert(questionIndex, sessionId); } } if (response != null) { encodedUrl = response.encodeURL(newURL.toString()); } else { encodedUrl = newURL.toString(); } } } else { encodedUrl = newURL.toString(); } //if (encodedUrl.indexOf("null") > 0) { //Debug.logError("in makeLink, controlPath:" + controlPath + " url:" + url, ""); //throw new RuntimeException("in makeLink, controlPath:" + controlPath + " url:" + url); //} //Debug.logInfo("Making URL, encode=" + encode + " for URL: " + newURL + "\n encodedUrl: " + encodedUrl, module); return encodedUrl; }
第五种类型是request-redirect-noparam,表明这也是一个请求重定向,类似request-redirect,但是不需要带上参数。参数可能是当前请求的QueryString,也有可能是redirect-parameter。
// <request-map uri="main"> // <response name="success" type="request-request-noparam" value="other"/> // </request-map> if ("request-redirect-noparam".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect with no parameters.", module); callRedirect(makeLink(request, response, nextRequestResponse.value), response, request); }
第六种类型是view,表明这是一个视图。视图需要在controller.xml中用view-map定义。
// <request-map uri="main"> // <response name="success" type="view" value="main"/> // </request-map> if ("view".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a view.", module); // check for an override view, only used if "success" = eventReturn String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn))) ? overrideViewUri : nextRequestResponse.value; renderView(viewName, requestMap.securityExternalView, request, response, saveName); }
第七种类型是view-last。
// <request-map uri="main"> // <response name="success" type="view-last" value="news"/> // </request-map> if ("view-last".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a view.", module); // check for an override view, only used if "success" = eventReturn String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn))) ? overrideViewUri : nextRequestResponse.value; // as a further override, look for the _SAVED and then _HOME and then _LAST session attributes Map<String, Object> urlParams = null; if (session.getAttribute("_SAVED_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_SAVED_VIEW_NAME_"); urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_SAVED_VIEW_PARAMS_")); } else if (session.getAttribute("_HOME_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_HOME_VIEW_NAME_"); urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_HOME_VIEW_PARAMS_")); } else if (session.getAttribute("_LAST_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_LAST_VIEW_NAME_"); urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_LAST_VIEW_PARAMS_")); } else if (UtilValidate.isNotEmpty(nextRequestResponse.value)) { viewName = nextRequestResponse.value; } if (urlParams != null) { for (Map.Entry<String, Object> urlParamEntry: urlParams.entrySet()) { String key = urlParamEntry.getKey(); // Don't overwrite messages coming from the current event if (!("_EVENT_MESSAGE_".equals(key) || "_ERROR_MESSAGE_".equals(key) || "_EVENT_MESSAGE_LIST_".equals(key) || "_ERROR_MESSAGE_LIST_".equals(key))) { request.setAttribute(key, urlParamEntry.getValue()); } } } renderView(viewName, requestMap.securityExternalView, request, response, null); }
第八种类型是view-last-noparam。
// <request-map uri="main"> // <response name="success" type="view-last-noparam" value="news"/> // </request-map> if ("view-last-noparam".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a view.", module); // check for an override view, only used if "success" = eventReturn String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn))) ? overrideViewUri : nextRequestResponse.value; // as a further override, look for the _SAVED and then _HOME and then _LAST session attributes if (session.getAttribute("_SAVED_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_SAVED_VIEW_NAME_"); } else if (session.getAttribute("_HOME_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_HOME_VIEW_NAME_"); } else if (session.getAttribute("_LAST_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_LAST_VIEW_NAME_"); } else if (UtilValidate.isNotEmpty(nextRequestResponse.value)) { viewName = nextRequestResponse.value; } renderView(viewName, requestMap.securityExternalView, request, response, null); }
第九种类型是view-home。
// <request-map uri="main"> // <response name="success" type="view-home" value="news"/> // </request-map> if ("view-home".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a view.", module); // check for an override view, only used if "success" = eventReturn String viewName = (UtilValidate.isNotEmpty(overrideViewUri) && (eventReturn == null || "success".equals(eventReturn))) ? overrideViewUri : nextRequestResponse.value; // as a further override, look for the _HOME session attributes Map<String, Object> urlParams = null; if (session.getAttribute("_HOME_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_HOME_VIEW_NAME_"); urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_HOME_VIEW_PARAMS_")); } if (urlParams != null) { for (Map.Entry<String, Object> urlParamEntry: urlParams.entrySet()) { request.setAttribute(urlParamEntry.getKey(), urlParamEntry.getValue()); } } renderView(viewName, requestMap.securityExternalView, request, response, null); }
第十种类型是none,表明不返回任何响应内容。
// <request-map uri="main"> // <response name="success" type="none"/> // </request-map> if ("none".equals(nextRequestResponse.type)) { // no view to render (meaning the return was processed by the event) if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is handled by the event.", module); }