Struts 2 - 升级后控制器映射错误
Struts 2 - wrong controller mapping after upgrade
从 Struts 版本 2.1.8.1 升级到 2.3.16.3 后,某些控制器的映射出现问题.升级前一切正常,但现在 60 多个控制器中有 3 个出现下一个错误:
ERROR [ActionComponent] Could not execute action: //vendor-tree!index
There is no Action mapped for namespace /
and action name vendor-tree!index
. - [unknown location]
此操作必须在 /vendor-tree!index
中而不是在 //vendor-tree!index
中,但我不明白为什么 Struts 只在错误的位置查找这 3 个控制器。
我根据
的每个请求都将其称为所有这些控制器
<#escape x as x?html>
<@s.action name="vendor-tree!index" executeResult="true" />
</#escape>
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
it doesn't change anything
Struts2: method attribute in <s:submit> button doesn't work is a different case, which not resolve this one
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.ui.theme" value="xhtml"/>
<constant name="struts.locale" value="bg_BG"/>
<constant name="struts.ui.templateDir" value="template" />
<constant name="struts.custom.i18n.resources" value="global, ajax, help, xhtml" />
<constant name="struts.devMode" value="false" />
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<constant name="struts.action.extension" value="html,xhtml,,xml,json" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="mymapper" class="stemo.ActionMapper.HierarchicalRestActionMapper"/>
<constant name="struts.mapper.class" value="mymapper"/>
<!--
enable the NamedVariablePatternMatcher to enable {variables} in the action namespace
-->
<bean type="com.opensymphony.xwork2.util.PatternMatcher" name="namedVariablePatternMatcher"
class="com.opensymphony.xwork2.util.NamedVariablePatternMatcher"/>
<constant name="struts.patternMatcher" value="namedVariablePatternMatcher"/>
<!--
<constant name="struts.convention.action.checkImplementsAction" value="false"/>
<constant name="struts.convention.action.checkAnnotation" value="false"/>
<constant name="struts.convention.action.defaultMethodName" value="index"/>
-->
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.default.parent.package" value="controllers"/>
<constant name="struts.convention.result.path" value="/"/>
<!-- constant name="struts.convention.package.locators.basePackage" value="stemo"/ -->
<constant name="struts.convention.package.locators" value="controllers"/>
<!-- Overwrite Convention -->
<package name="controllers" extends="struts-default" namespace="/" >
<interceptors>
<interceptor name="storeMessages" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<!--interceptor name="messMessage" class="stemo.ActionMapper.RedirectMessageInterceptor"/ -->
<interceptor name="cachingHeadersInterceptor" class="stemo.ActionMapper.CachingHeadersInterceptor"/>
<interceptor name="myCookie" class="stemo.ActionMapper.CookiesInterceptor"/>
<interceptor name="myLogger" class="stemo.ActionMapper.LoggerInterceptor"/>
<interceptor-stack name="myStack">
<!-- interceptor-ref name="messMessage"/ -->
<interceptor-ref name="storeMessages" >
<param name="operationMode">AUTOMATIC</param>
</interceptor-ref>
<interceptor-ref name="myCookie"/>
<interceptor-ref name="myLogger"/>
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack"/>
</package>
<!-- for JBoss -->
<constant name="struts.convention.exclude.parentClassLoader" value="true" />
<constant name="struts.convention.action.fileProtocols" value="jar,vfsfile,vfszip" />
<constant name="struts.convention.classLoader.excludeParent" value="false" />
<!-- for Struts version 2.3.18+ -->
<constant name="struts.multipart.parser" value="jakarta-stream" />
<constant name="struts.multipart.maxSize" value="50242880" />
<!--
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.views.freemarker.FreemarkerManager" />
-->
<constant name="struts.freemarker.manager.classname" value="customFreemarkerManager"/>
<constant name="struts.freemarker.mru.max.strong.size" value="250" />
<constant name="struts.freemarker.templatesCache.updateDelay" value="1800" />
<package name="json" extends="json-default">
<action name="brand-ajax" class="stemo.json.BrandAjaxController">
<result type="json">
<param name="root">model</param>
<param name="excludeProperties">LHarakt, Harakt</param>
</result>
</action>
<action name="vendor-ajax" class="stemo.json.VendorAjaxController">
<result type="json">
<param name="root">model</param>
<param name="excludeProperties">LHarakt, Harakt</param>
</result>
</action>
</package>
</struts>
问题控制器
package stemo.controllers;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.ObjectUtils;
import stemo.hibernate.model.DAO.VendorDAO;
import stemo.hibernate.model.entity.Vendor;
import stemo.hibernate.model.filters.*;
import java.util.List;
import java.util.Map;
@Results({
@Result(name = "success", type = "freemarker", location = "/vendortree.ftl")
})
public class VendorTreeController implements ModelDriven<Object>, SessionAware
{
private VendorDAO vendorDAO;
private List<Vendor> vendors;
private String sklad_check;
private String promo_check;
private String newprice_check;
private String lowprice_check;
private String newproduct_check;
private String oldproduct_check;
private String withgift_check;
private String sch_check;
private FilterManager filters = null;
private Map<String, Object> sessionMap;
@Autowired
public void setVendorDAO(VendorDAO vendorDAO)
{
this.vendorDAO = vendorDAO;
}
private FilterManager initFilters()
{
if (filters != null)
{
return filters;
}
filters = new FilterManager();
if (!ObjectUtils.isEmpty(sklad_check))
{
StoreFilter f = new StoreFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(promo_check))
{
PromoFilter f = new PromoFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(newproduct_check))
{
NewProductFilter f = new NewProductFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(withgift_check))
{
ProductsWithGift f = new ProductsWithGift();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(newprice_check))
{
ProductsWithNewPrices f = new ProductsWithNewPrices();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(lowprice_check))
{
ProductsWithLowerPrices f = new ProductsWithLowerPrices();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(oldproduct_check))
{
OldProductsFilter f = new OldProductsFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(sch_check))
{
SecondHandFilter f = new SecondHandFilter();
filters.addFilter(f);
}
return filters;
}
public HttpHeaders index()
{
sklad_check = (String) sessionMap.get("sklad_check");
promo_check= (String) sessionMap.get("promo_check");
newprice_check = (String) sessionMap.get("newprice_check");
lowprice_check = (String) sessionMap.get("lowprice_check");
newproduct_check = (String) sessionMap.get("newproduct_check");
oldproduct_check = (String) sessionMap.get("oldproduct_check");
withgift_check = (String) sessionMap.get("withgift_check");
sch_check = (String) sessionMap.get("sch_check");
FilterManager filters = initFilters();
vendors = vendorDAO.findAll(filters);
return new DefaultHttpHeaders("success").disableCaching();
}
@Override
public Object getModel()
{
return vendors;
}
@Override
public void setSession(Map<String, Object> stringObjectMap)
{
sessionMap = stringObjectMap;
}
}
有效控制器
package stemo.controllers;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.model.DAO.PenaltyDAO;
import stemo.hibernate.model.entity.PenaltyInfo;
import stemo.hibernate.model.entity.User;
import stemo.hibernate.model.entity.date.MonthItem;
import java.util.List;
import java.util.Map;
@Results({
@Result(name = "success", type = "freemarker", location = "/penalty_details.ftl"),
@Result(name = "list", type = "freemarker", location = "/penalty_all_details.ftl"),
@Result(name = "error", type = "redirect", location = "/products")
})
public class PenaltyController implements ModelDriven<Object>, SessionAware
{
private Map<String, Object> sessionMap;
private List<PenaltyInfo> penaltyInfo;
private String date;
private Double penalty;
private PenaltyDAO penaltyDAO;
@Autowired
public void setPenaltyDAO(PenaltyDAO penaltyDAO)
{
this.penaltyDAO = penaltyDAO;
}
@Override
public void setSession(Map<String, Object> stringObjectMap)
{
this.sessionMap = stringObjectMap;
}
@Override
public Object getModel()
{
return penaltyInfo;
}
public String getDate()
{
return date;
}
public void setDate(String date)
{
this.date = date;
}
public Double getPenalty()
{
return penalty;
}
//GET
public HttpHeaders index()
{
User user = (User) sessionMap.get("user");
if ((user != null))
{
//months = MonthItem.getMonths(null);
if (date == null)
{
date = MonthItem.getCurrent();
}
penaltyInfo = penaltyDAO.getList(user,MonthItem.toDate(date));
penalty = penaltyDAO.getPenalty(user,MonthItem.toDate(date));
return new DefaultHttpHeaders("success").disableCaching();
}
else
return new DefaultHttpHeaders("error").disableCaching();
}
public HttpHeaders list()
{
User user = (User) sessionMap.get("user");
if ((user != null))
{
penaltyInfo = penaltyDAO.getAllList(user);
penalty = user.getPenalty();
return new DefaultHttpHeaders("list").disableCaching();
}
else
return new DefaultHttpHeaders("error").disableCaching();
}
}
自定义映射器是
public class HierarchicalRestActionMapper extends RestActionMapper {
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
Set<String> actions = new HashSet<String>();
PackageConfig packageConfig;
Configuration configuration = configManager.getConfiguration();
// get the current set of action names in our packages of interest
Map<String, PackageConfig> packageConfigs = configuration.getPackageConfigs();
for (Map.Entry<String, PackageConfig> packageEntry : packageConfigs.entrySet()) {
packageConfig = packageEntry.getValue();
actions.addAll(packageConfig.getActionConfigs().keySet());
}
// parse the uri into namespace, action, method and id
Map<String, String> params = URIActionMethodParser.process(uri, actions);
if (params != null) {
// update the mapping
mapping.setNamespace(params.get("namespace"));
params.remove("namespace");
mapping.setName(params.get("action"));
params.remove("action");
mapping.setParams((Map) params);
}
}
}
你知道为什么吗?
当我在没有 input 方法的情况下执行动作标记时
<@s.action name="vendor-tree" executeResult="true"/>
Struts 抛出缺少 execute 方法错误。我将我的 input 方法重命名为 execute 现在一切正常。
从 Struts 版本 2.1.8.1 升级到 2.3.16.3 后,某些控制器的映射出现问题.升级前一切正常,但现在 60 多个控制器中有 3 个出现下一个错误:
ERROR [ActionComponent] Could not execute action:
//vendor-tree!index
There is no Action mapped for namespace/
and action namevendor-tree!index
. - [unknown location]
此操作必须在 /vendor-tree!index
中而不是在 //vendor-tree!index
中,但我不明白为什么 Struts 只在错误的位置查找这 3 个控制器。
我根据
<#escape x as x?html>
<@s.action name="vendor-tree!index" executeResult="true" />
</#escape>
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
it doesn't change anythingStruts2: method attribute in <s:submit> button doesn't work is a different case, which not resolve this one
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.ui.theme" value="xhtml"/>
<constant name="struts.locale" value="bg_BG"/>
<constant name="struts.ui.templateDir" value="template" />
<constant name="struts.custom.i18n.resources" value="global, ajax, help, xhtml" />
<constant name="struts.devMode" value="false" />
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<constant name="struts.action.extension" value="html,xhtml,,xml,json" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="mymapper" class="stemo.ActionMapper.HierarchicalRestActionMapper"/>
<constant name="struts.mapper.class" value="mymapper"/>
<!--
enable the NamedVariablePatternMatcher to enable {variables} in the action namespace
-->
<bean type="com.opensymphony.xwork2.util.PatternMatcher" name="namedVariablePatternMatcher"
class="com.opensymphony.xwork2.util.NamedVariablePatternMatcher"/>
<constant name="struts.patternMatcher" value="namedVariablePatternMatcher"/>
<!--
<constant name="struts.convention.action.checkImplementsAction" value="false"/>
<constant name="struts.convention.action.checkAnnotation" value="false"/>
<constant name="struts.convention.action.defaultMethodName" value="index"/>
-->
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.default.parent.package" value="controllers"/>
<constant name="struts.convention.result.path" value="/"/>
<!-- constant name="struts.convention.package.locators.basePackage" value="stemo"/ -->
<constant name="struts.convention.package.locators" value="controllers"/>
<!-- Overwrite Convention -->
<package name="controllers" extends="struts-default" namespace="/" >
<interceptors>
<interceptor name="storeMessages" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<!--interceptor name="messMessage" class="stemo.ActionMapper.RedirectMessageInterceptor"/ -->
<interceptor name="cachingHeadersInterceptor" class="stemo.ActionMapper.CachingHeadersInterceptor"/>
<interceptor name="myCookie" class="stemo.ActionMapper.CookiesInterceptor"/>
<interceptor name="myLogger" class="stemo.ActionMapper.LoggerInterceptor"/>
<interceptor-stack name="myStack">
<!-- interceptor-ref name="messMessage"/ -->
<interceptor-ref name="storeMessages" >
<param name="operationMode">AUTOMATIC</param>
</interceptor-ref>
<interceptor-ref name="myCookie"/>
<interceptor-ref name="myLogger"/>
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack"/>
</package>
<!-- for JBoss -->
<constant name="struts.convention.exclude.parentClassLoader" value="true" />
<constant name="struts.convention.action.fileProtocols" value="jar,vfsfile,vfszip" />
<constant name="struts.convention.classLoader.excludeParent" value="false" />
<!-- for Struts version 2.3.18+ -->
<constant name="struts.multipart.parser" value="jakarta-stream" />
<constant name="struts.multipart.maxSize" value="50242880" />
<!--
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.views.freemarker.FreemarkerManager" />
-->
<constant name="struts.freemarker.manager.classname" value="customFreemarkerManager"/>
<constant name="struts.freemarker.mru.max.strong.size" value="250" />
<constant name="struts.freemarker.templatesCache.updateDelay" value="1800" />
<package name="json" extends="json-default">
<action name="brand-ajax" class="stemo.json.BrandAjaxController">
<result type="json">
<param name="root">model</param>
<param name="excludeProperties">LHarakt, Harakt</param>
</result>
</action>
<action name="vendor-ajax" class="stemo.json.VendorAjaxController">
<result type="json">
<param name="root">model</param>
<param name="excludeProperties">LHarakt, Harakt</param>
</result>
</action>
</package>
</struts>
问题控制器
package stemo.controllers;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.ObjectUtils;
import stemo.hibernate.model.DAO.VendorDAO;
import stemo.hibernate.model.entity.Vendor;
import stemo.hibernate.model.filters.*;
import java.util.List;
import java.util.Map;
@Results({
@Result(name = "success", type = "freemarker", location = "/vendortree.ftl")
})
public class VendorTreeController implements ModelDriven<Object>, SessionAware
{
private VendorDAO vendorDAO;
private List<Vendor> vendors;
private String sklad_check;
private String promo_check;
private String newprice_check;
private String lowprice_check;
private String newproduct_check;
private String oldproduct_check;
private String withgift_check;
private String sch_check;
private FilterManager filters = null;
private Map<String, Object> sessionMap;
@Autowired
public void setVendorDAO(VendorDAO vendorDAO)
{
this.vendorDAO = vendorDAO;
}
private FilterManager initFilters()
{
if (filters != null)
{
return filters;
}
filters = new FilterManager();
if (!ObjectUtils.isEmpty(sklad_check))
{
StoreFilter f = new StoreFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(promo_check))
{
PromoFilter f = new PromoFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(newproduct_check))
{
NewProductFilter f = new NewProductFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(withgift_check))
{
ProductsWithGift f = new ProductsWithGift();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(newprice_check))
{
ProductsWithNewPrices f = new ProductsWithNewPrices();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(lowprice_check))
{
ProductsWithLowerPrices f = new ProductsWithLowerPrices();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(oldproduct_check))
{
OldProductsFilter f = new OldProductsFilter();
filters.addFilter(f);
}
if (!ObjectUtils.isEmpty(sch_check))
{
SecondHandFilter f = new SecondHandFilter();
filters.addFilter(f);
}
return filters;
}
public HttpHeaders index()
{
sklad_check = (String) sessionMap.get("sklad_check");
promo_check= (String) sessionMap.get("promo_check");
newprice_check = (String) sessionMap.get("newprice_check");
lowprice_check = (String) sessionMap.get("lowprice_check");
newproduct_check = (String) sessionMap.get("newproduct_check");
oldproduct_check = (String) sessionMap.get("oldproduct_check");
withgift_check = (String) sessionMap.get("withgift_check");
sch_check = (String) sessionMap.get("sch_check");
FilterManager filters = initFilters();
vendors = vendorDAO.findAll(filters);
return new DefaultHttpHeaders("success").disableCaching();
}
@Override
public Object getModel()
{
return vendors;
}
@Override
public void setSession(Map<String, Object> stringObjectMap)
{
sessionMap = stringObjectMap;
}
}
有效控制器
package stemo.controllers;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.model.DAO.PenaltyDAO;
import stemo.hibernate.model.entity.PenaltyInfo;
import stemo.hibernate.model.entity.User;
import stemo.hibernate.model.entity.date.MonthItem;
import java.util.List;
import java.util.Map;
@Results({
@Result(name = "success", type = "freemarker", location = "/penalty_details.ftl"),
@Result(name = "list", type = "freemarker", location = "/penalty_all_details.ftl"),
@Result(name = "error", type = "redirect", location = "/products")
})
public class PenaltyController implements ModelDriven<Object>, SessionAware
{
private Map<String, Object> sessionMap;
private List<PenaltyInfo> penaltyInfo;
private String date;
private Double penalty;
private PenaltyDAO penaltyDAO;
@Autowired
public void setPenaltyDAO(PenaltyDAO penaltyDAO)
{
this.penaltyDAO = penaltyDAO;
}
@Override
public void setSession(Map<String, Object> stringObjectMap)
{
this.sessionMap = stringObjectMap;
}
@Override
public Object getModel()
{
return penaltyInfo;
}
public String getDate()
{
return date;
}
public void setDate(String date)
{
this.date = date;
}
public Double getPenalty()
{
return penalty;
}
//GET
public HttpHeaders index()
{
User user = (User) sessionMap.get("user");
if ((user != null))
{
//months = MonthItem.getMonths(null);
if (date == null)
{
date = MonthItem.getCurrent();
}
penaltyInfo = penaltyDAO.getList(user,MonthItem.toDate(date));
penalty = penaltyDAO.getPenalty(user,MonthItem.toDate(date));
return new DefaultHttpHeaders("success").disableCaching();
}
else
return new DefaultHttpHeaders("error").disableCaching();
}
public HttpHeaders list()
{
User user = (User) sessionMap.get("user");
if ((user != null))
{
penaltyInfo = penaltyDAO.getAllList(user);
penalty = user.getPenalty();
return new DefaultHttpHeaders("list").disableCaching();
}
else
return new DefaultHttpHeaders("error").disableCaching();
}
}
自定义映射器是
public class HierarchicalRestActionMapper extends RestActionMapper {
protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {
Set<String> actions = new HashSet<String>();
PackageConfig packageConfig;
Configuration configuration = configManager.getConfiguration();
// get the current set of action names in our packages of interest
Map<String, PackageConfig> packageConfigs = configuration.getPackageConfigs();
for (Map.Entry<String, PackageConfig> packageEntry : packageConfigs.entrySet()) {
packageConfig = packageEntry.getValue();
actions.addAll(packageConfig.getActionConfigs().keySet());
}
// parse the uri into namespace, action, method and id
Map<String, String> params = URIActionMethodParser.process(uri, actions);
if (params != null) {
// update the mapping
mapping.setNamespace(params.get("namespace"));
params.remove("namespace");
mapping.setName(params.get("action"));
params.remove("action");
mapping.setParams((Map) params);
}
}
}
你知道为什么吗?
当我在没有 input 方法的情况下执行动作标记时 <@s.action name="vendor-tree" executeResult="true"/>
Struts 抛出缺少 execute 方法错误。我将我的 input 方法重命名为 execute 现在一切正常。