I'm working with a Struts 2 webapp with the following action mapping:
<action name="something_*" class="foo.whatever.MyAction" method="{1}">
<result>blah/myJsp.jsp</result>
...
</action>
So if I load the URL /something_load.action, it calls to MyAction.load(), and so on. Piece of cake. What puzzles me is that loading /something.action does work too (I guess it's invoking the execute() method). How is it possible? My action mapping is supposed to match "something_", but there is no underescore in my URL. It should give me an error! Shouldn't it?
I've double checked that there isn't another mapping for "something.action" in the struts config files. I also checked the web.xml file, just in case...
The only explanation that I can imagine is that the underscore is ignored in Struts if I use wildcard mappings. But then it would make no difference to load /something_load.action, /some_thing_lo_ad.action... and that's not true.
I'm aware that this must be a very noobish question, but I've been unable to solve the mistery, neither looking through Stackoverflow questions nor the Struts documentation.
This is the main struts.xml file:
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="false" />
<constant name="struts.freemarker.templatesCache" value="true" />
<package name="default" extends="struts-default">
<!-- interceptors ... -->
<!-- global results for error pages -->
</package>
<!-- lots of includes -->
</struts>
It appears that wildcards are matched loosely in order to support some legacy syntax. So the issue isn't with underscore but with loose matching pattern.
From the javadocs:
Patterns can optionally be matched "loosely". When the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard, wildcard), if the pattern fails, it is also matched as if the last two characters didn't exist. The goal is to support the legacy "*!*" syntax, where the "!*" is optional.
Related
In struts2, is it possible to have an action name with a space?
I'm using the RegexPatternMatcher
<constant name="struts.patternMatcher" value="regex"/>
<constant name="struts.enable.SlashesInActionNames" value="true"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/>
My action is defined as
<action name="/users/{username}"
method="execute"
class="com.test.UserAction">
<result name="success" type="tiles">.test.user</result>
</action>
When I try a url like, http://localhost:8080/users/a%20space
The a%20space is set as aspace in the model. The %20 is not escaped and is just removed. I've also tried http://localhost:8080/users/a+space and the same thing happens.
Environment
Struts2 version, 2.3.15.1
You can allow spaces in action names by setting the parameter in the struts configuration,
struts.allowed.action.names
The default is
[a-zA-Z0-9._!/\-]*
The DefaultActionMapper uses this regex to validate the action name. If it doesn't match, it'll try to clean it up.
So modifying the setting to
<constant name="struts.allowed.action.names" value="[ a-zA-Z0-9._!/\-]*"/>
allows spaces
I am facing a problem that I couldn't find a key word for googleing the following situation:
<package name="Main" namespace="/" extends="struts-default">
<action name="administrator" class="com.xyz.AdminAction">
<result name="success">/WEB-INF/jsp/admin.jsp</result>
</action>
</package>
The above url should be http://xyz.com/administrator and it works fine. However, if I change the url to http://xyz.com/asdasd/asdasdasd/administrator and it still works, but I can't accept this! So any setting to tell struts only http://xyz.com/administrator is accepted? Thanks!
Set the alwaysSelectFullNamespace property to true.
From struts-default.properties:
### Whether to always select the namespace to be everything before the last slash or not
struts.mapper.alwaysSelectFullNamespace=true
XML configuration is preferred, so in your struts.xml:
<struts>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />
...
I'm having an issue removing the #Action and #Result Convention plugin annotations from an action and replacing them with the equivalent config in struts.xml.
package com.microed.cars.web;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
import com.opensymphony.xwork2.ActionSupport;
public class HomeAction extends ActionSupport {
#Action(results = {
#Result(location = "/jsp/home.jsp")
})
#Override
public String execute() throws Exception {
return super.execute();
}
}
When these annotations are there, I can successfully access localhost:port/context/home.action
When I remove the annotations I get 'no result defined for action..... ' struts error, despite there being a 'capture all' result in struts.xml - the entire struts.xml is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constant name="struts.devMode" value="true"/>
<constant name="struts.convention.package.locators" value="web"/>
<constant name="struts.convention.default.parent.package" value="beetroot"/>
<package name="beetroot" extends="json-default">
<action name="home" class="homeAction">
<result>/jsp/home.jsp</result>
</action>
<action name="cars" class="baseCarsAction">
<result name="input" type="json">
<param name="root">autoResults</param>
/jsp/home.jsp
</result>
</action>
</package>
</struts>
It extends json-default because I need the json result type for an autocomplete function.
I don't know why it's not picking up the action mapping for the homeAction class. I know struts.xml is being read because if I remove the action mapping "cars" then the autocomplete is disabled (but this needs the annotations which I'm trying to remove in order to validate this).
I know that 'no result defined' is a simple error, usually caused by spelling/capitalization errors but this is definitely not the case here, it's simply seems to be ignoring the whole "home" action mapping.
When stepping through DefaultActionInvocation.createResult, there are no 'results' at all for it to try to match against.
As it stands the cars action declaration isn't valid (nor does it make sense, IMO):
<action name="cars" class="baseCarsAction">
<result name="input" type="json">
<param name="root">autoResults</param>
<param name="location">/jsp/home.jsp</param>
</result>
</action>
That said: if it's a JSON result, a JSP isn't helpful, and it'll be ignored (or downright rejected, I'm not sure if it's an error or not). A single result will be either JSON, or HTML.
Turn logging up to DEBUG level to catch startup errors to narrow the range of possible causes.
If baseAction is configured in your Spring config file (which is unnecessary if you're using Spring annotations for injection) the configuration for the home action is valid.
I'd be wary of deploying the convention plugin if you're not actually using it: it changes how actions are mapped; it may have an impact on the surrounding application and cause problems. Stick with one or the other, avoid both–it makes it harder to reason about the source of application behavior.
Unrelated, but I recommend putting JSP pages under /WEB-INF to disallow direct client access.
I have all the jsp pages (ex: index.jsp) in the WebContent folder and my application is working smoothly.
Now, I created a folder "pages" inside WebContent, where I transferred all my jsp pages.
And I added the following line inside the <struts> tags:
<constant name="struts.convention.result.path" value="/pages" />
But still, its not searching for jsp inside the "pages" folder.
I am getting:
HTTP Status 404 - /MyApplicationName/index.jsp
Where am I doing a mistake?
EDIT: The current structure of my application is as follows:
The welcome page is an action instead of a jsp page. The result page of this action is index.jsp. This action populates the dropdown menu and some other fields in index.jsp. Now, I want to keep index.jsp along with other jsp pages in the folder "WebContent/pages". I have over 30 jsp pages in my application, and I don't want to mention the full path of every jsp in the result-tag. Thats why I want to make use of the struts2 constant "struts.convention.result.path"
My question is, why the code I have mentioned above is not working?
Accessing index.jsp directly bypasses all S2 request handling.
There's no S2 or S2 Convention plugin involved, you're just accessing a JSP page.
If index.jsp is first page of your application then you need to change its path to /pages/index.jsp in <welcome-file> tag of web.xml. And for rest I agree with #Dave Newton :)
Probably your mistake was in struts.xml.
When you create a new-Folder under web-content make with the same name (folder-name) of
xml file under src and include it in struts.xml file it will work fine.
For eg: suppose you created example folder under web-content, now create example.xml file
under src folder and in struts.xml file include example.xml
example.xml:
<struts>
<package name="example" namespace="/example" extends="default">
<action name="your action name" class="your class" method="your method">
</action>
</package>
</struts>
struts.xml:
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="false" />
<action name="" class="" method="">
</action>
</package>
<include file="example.xml"/>
In struts2 I am writing an app where I need to make sure that the url redirection works the same whether or not there is a trailing slash at the end.
E.g. example.com/app should behave same way as if user entered example.com/app/. Currently I changed mapping in struts.xml like so -
<struts>
<package name="default" namespace="/" extends="secure">
<interceptors> ... <interceptors>
<action name="app">
<result type="redirectAction">/app/</result>
</action>
</package>
</struts>
and
<struts>
<package name="app" namespace="/app" extends="secure">
<interceptors> ... <interceptors>
<action name="" class="com.example.action.app.LoginAction" method="display">
<interceptor-ref name="store">
<param name="operationMode">RETRIEVE</param>
</interceptor-ref>
<interceptor-ref name="basic" />
<result type="redirectAction">home</result>
<result name="display">/jsp/base/content/login/loginForm.jsp</result>
</action>
</package>
</struts>
But this seems hackish since if I go to example.com/app it will show example.com//app/.html in the URL.
Any help appreciated.
Answer was derived in comments under the answer.
Quaternion:
Personally I would write all my urls with out the trailing slash...
and then I would use something external to the application to rewrite
urls as appropriate, perhaps iptables could determine if there is a
trailing slash and if so always strip it.
Mohana Rao SV:
As suggested above follow without tailing slash. And override
StrutsPrepareAndExecuteFilter one of the filter job is from the url it
has to identify the namespace and action name and invoke respective
action. So here remove tailing slash from url.
Quaternion:
In namespace "/" you have an action called app. That is all there is
to it to invoke CONTEXT_ROOT/app (that is what struts2 expects), you
don't ever expect to see a "/" on the end of the url, so you want to
find a method that parses the url before struts2 resolves the mapping.
What you have described only requires something to remove a trailing
"/" if it exists. I'd look to iptables because I've used it before or
some other url rewriter... Mahana would keep it all part of the web
app and use a filter, methods differ but the effect is the same.