这里的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);
}