Struts2在web.xml中配置為“/*”和“*.action,*.jsp”的差別

一隻小螞蟻吆發表於2020-04-06

問題:
1、Struts2在web.xml中配置為“/*”和“*.action,*.jsp”的差別。
2、There is no Action mapped for namespace / and action name ...的問題。

分析(環境是Struts2.1.8.1):
Struts2過濾器的配置有2種方式:

<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
 


那麼這兩種方式的配置,究竟有什麼差別呢?
首先,假如配置方式是*.action的話,一般應當同時配置*.jsp,因為如果不通過action而直接訪問jsp頁面的話,Struts2標籤在解析的時候會獲取當前執行緒ThreadLocal中的Dispatcher。而Dispatcher是在Struts過濾器中預設的。程式碼如下:

public static ValueStack getStack(PageContext pageContext) {
HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
ValueStack stack = (ValueStack) req.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
if (stack == null) {
HttpServletResponse res = (HttpServletResponse) pageContext.getResponse();
Dispatcher du = Dispatcher.getInstance();
if (du == null) {
throw new ConfigurationException("The Struts dispatcher cannot be found.  This is usually caused by "+
"using Struts tags without the associated filter. Struts tags are only usable when the request "+
"has passed through its servlet filter, which initializes the Struts dispatcher needed for this tag.");
}
//略...
 



除了為當前執行緒預設Dispatcher以外,Struts2對“/*”的請求,在完成普通的“*.action”過濾的基礎上,另外提供2點功能:
第1點用於訪問classpath中特定的靜態資源;
第2點支援無字尾名的Action請求;
Struts2的標籤有時候需要某些CSS、JS檔案的支援,比如<s:head/>標籤,可能就轉換成:

<link rel="stylesheet" href="/demo/struts/xhtml/styles.css" type="text/css"/>
<script src="/demo/struts/utils.js" type="text/javascript"></script>
 
第1點功能帶來的好處是可以把這些Struts2框架用到的CSS、JS檔案打包在Struts2-core-***.jar檔案中分發,使得Struts2的釋出包對開發人員而言更加簡潔。
下面看一下StrutsPrepareAndExecuteFilter是怎樣實現的:
①將Dispatcher預設到執行緒的ThreadLocal變數上;
②對於Action請求,直接execute.executeAction(request,response,mapping);
③如果是/struts、或者/static開始的資源,則在classpath下查詢特定的包下面的匹配資源;
④其他的所有資源(包括直接訪問的JSP、以及其他靜態資源)轉交過濾器鏈的下一個環節處理:chain.doFilter(request, response);
上面所說的特定包,是指在
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
<init-param>
<param-name>packages</param-name>
<param-value>eee</param-value>
</init-param>
</filter>
 


中通過packages引數指定的包、以及 "org.apache.struts2.static template org.apache.struts2.interceptor.debugging static"這4個包。
由於packages可配置,從而,如果有自己的classpath上的資源需要訪問,或者需要更改Struts本身的靜態資源時,只要把Classpath下相應的package設定在過濾器的初始引數中即可(這一條看上去好像沒什麼用處)。

上面是使用/*時對靜態資源的訪問,那麼使用*.action時如果需要的話,如何訪問靜態資源呢?
很簡單,只要把需要用到的靜態資源解壓縮到WebContent/struts目錄下即可。

第2點“支援無字尾名的Action請求”經常帶來一些混亂,最典型的就是“/*”錯誤地攔截了其他的對映為無字尾名的Servlet請求。比如DWR、FCKEditor等都存在這種問題。
比如,當訪問“/demo/dwr”時,正常情況應該顯示當前系統中對外暴露的JS方法的列表,但在Struts2的預設配置下,卻得到“There is no Action mapped for namespace / and action name dwr.”
又比如在預設配置下,訪問http://localhost:8080/demo/hello.action
和訪問http://localhost:8080/demo/hello這兩者是等同的。
當然,也只有無字尾名的URL請求才會被Struts2當做是Action,這也是為什麼/dwr無法訪問,然而/dwr/interface.js可以訪問的原因。

具體的,看一下下面的程式碼就明白了:

//Struts2預設將“*.action”或者無字尾的URL當做Action
protected List<String> extensions = new ArrayList<String>() {{ add("action"); add("");}};
protected String dropExtension(String name, ActionMapping mapping) {
if (extensions == null) {
return name;
}
for (String ext : extensions) {
if ("".equals(ext)) {
// This should also handle cases such as /foo/bar-1.0/description. It is tricky to distinquish /foo/bar-1.0 but perhaps adding a numeric check in the future could work
// request uri如果不包含副檔名的話,則匹配此情況
int index = name.lastIndexOf('.');
if (index == -1 || name.indexOf('/', index) >= 0) {
return name;
}
} else {
String extension = "." + ext;
if (name.endsWith(extension)) {
name = name.substring(0, name.length() - extension.length());
mapping.setExtension(ext);
return name;
}
}
}
return null;
}

 
那麼,怎麼解決此問題呢?
有2種辦法。
第1種很簡單,在Struts.properties中定義:
struts.action.extension = action即可解決此問題。
Struts2預設配置對應於:
struts.action.extension = action,(注意後面有個逗號)
第2種是在Struts.properties中設定:
struts.action.excludePattern = /dwr.*,/webEditor.*(注意,這兒是正規表示式,不是URL匹配模式,所以要寫/dwr.*而不是/dwr/*)
這種寫法應配置StrutsPrepareAndExecuteFilter,配置FilterDispatcher是無效的。
在微博管家專案中,採用的是struts2.1.8,出現了jsp、js、css等檔案載入失敗等問題。通過以下配置解決:
struts.xml:
<constant name="struts.action.extension" value="" />
struts.properties:
struts.locale=zh_CN
struts.i18n.encoding=utf-8
struts.action.extension=,
struts.action.excludePattern = /js.*,/static.*(js和static資料夾是專案中js和css所在的目錄)

相關文章