Struts2 FAQ[筆記]
1,怎樣顯示用字節數組提供的動態或靜態圖片:o display an image represented as an array of bytes
- Reference an Action for the image src attribute
- Provide a helper method on the Action to return an array of bytes
- Provide a Result Type that renders the array to the response
MyAction.jsp
<img src="/myWebAppContext/myAction.do" />
<img src="/myWebAppContext/myAction.do" />
Provide Helper Methods
public class MyAction extends ActionSupport {
public String doDefault() {
return "myImageResult";
}
public byte[] getMyImageInBytes() { .... }
public String getMyContentType() { ... }
public String getMyContentDisposition() { ... }
public int getMyContentLength() { .... }
public int getMyBufferSize() { ... }
}
Provide a Custom Result Type
<struts>
...
<result-types>
<result-type name="myBytesResult" class="MyBytesResult" />
</result-types>
...
<action name="myAction" class="MyAction">
<result name="myImageResult" type="myBytesResult">
<param name="contentType">${myContentType}</param>
<param name="contentDisposition">${myContentDisposition}</param>
<param name="contentLength">${myContentLength}</param>
<param name="bufferSize">${myBufferSize}</param>
<result>
</action>
...
</struts>
public class MyBytesResult implements Result {
public void execute(ActionInvocation invocation) throws Exception {
MyAction action = (MyAction) invocation.getAction();
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType(action.getContentType());
response.setContentLength(action.getContentLength());
response.getOutputStream().write(action.getImageInBytes());
response.getOutputStream().flush();
}
}
2,怎樣測試Struts2的web應用
First, push as much business and data access logic as possible out of the Action class and into a POJO facade that the Action can call. In this way, you can test all that outside of the Action. The MailReader is a fair example of this, although the data access API is quirky and could be streamlined.
首先,把業務和數據訪問邏輯分離出去,并且放到能夠被Action調用的POJO facade里。這樣,你可以在Action的外面測試這部分內容。
After that, what's really left to test is the UI. For UI tests, a very good choice is Selenium.
之后,需要測試UI,對于UI測試,Selenium是個非常好的選擇。
The Selenium IDE (a FireFox plugin) includes a recorder to help jump start the tests, and then you can go back and edit the tests, and also add test that can't be easily recorded. (Like is a control disabled or text not present.)
The best part is that the initial tests can be recorded in HTML and the convert to real Java code. The Java code can then be run as part of a standard JUnit test (if the Selenium proxy server is running in the background).
最好的部分是,初始化測試在Html能被錄制下來,并且轉化成真正的Java代碼。Java代碼能夠作為標準的JUnit來測試(只要Selenium代理服務器在后臺運行)
3,怎樣測試Action?
The Struts 2 Action doesn't expose HTTP in its signature. When access to HTTP resources is needed, references to these resources can be injected at runtime, but, to the Action, the servlet resources appear as ordinary Maps. When running tests outside of the framework, the test scaffolding can create and set a Map with whatever objects are needed by the test.
Struts2 Action在簽名上不依賴預HTTP。當訪問需要的HTTP是,這些資源在運行時能構被注入。Action,servlet資源看上區就像通用的Maps。當在Framework外部測試時,測試平臺會建立所有需要的任何對象的映射。
There are several approaches to unit testing Struts 2-based applications, whether using JUnit or TestNG
.
Two popular techniques are direct Action invocation and testing
interceptors and result-types independently. We will demonstrate both
approaches using JUnit 3.
兩個流行的技術是獨立地測試直接的Action調用和interceptor和Result-Type。
Direct Action Invocation
The simplest approach is to instantiate your Actions, call the appropriate setters, then invoke execute. Calling the Action directly allows you to bypass all the complicated container setup.
最簡單的手段是實例化Action,調用相關的Setter,然后調用excute。調用Action直接允許你繞過復雜的容器啟動過程。
package org.petsoar.actions.inventory;
import com.mockobjects.constraint.IsEqual;
import com.mockobjects.dynamic.C;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork.Action;
import junit.framework.TestCase;
import org.petsoar.pets.Pet;
import org.petsoar.pets.PetStore;
public class TestViewPet extends TestCase {
private Mock mockPetStore;
private ViewPet action;
protected void setUp() throws Exception {
mockPetStore = new Mock(PetStore.class);
PetStore petStore = (PetStore) mockPetStore.proxy();
action = new ViewPet();
action.setPetStore(petStore);
}
public void testViewPet() throws Exception {
Pet existingPet = new Pet();
existingPet.setName("harry");
existingPet.setId(1);
Pet expectedPet = new Pet();
expectedPet.setName("harry");
expectedPet.setId(1);
mockPetStore.expectAndReturn("getPet", C.args(new IsEqual(new Long(1))), existingPet);
action.setId(1);
String result = action.execute();
assertEquals(Action.SUCCESS, result);
assertEquals(expectedPet, existingPet);
mockPetStore.verify();
}
public void testViewPetNoId() throws Exception {
mockPetStore.expectAndReturn("getPet", C.ANY_ARGS, null);
String result = action.execute();
assertEquals(Action.ERROR, result);
assertEquals(1, action.getActionErrors().size());
assertEquals("Invalid pet selected.", action.getActionErrors().iterator().next());
assertNull(action.getPet());
mockPetStore.verify();
}
public void testViewPetInvalidId() throws Exception {
action.setId(-1);
testViewPetNoId();
}
}
Testing Interceptors and/or Result Types
Check out the test suites in XWork and Struts 2. These suites are comprehensive and provide a good starting point. Here's an example.
取出XWork和Struts2的測試套件,這些測試套件是復雜的,并且提供了好的起點。
public void testDoesNotAllowMethodInvocations() {
Map params = new HashMap();
params.put("@java.lang.System@exit(1).dummy", "dumb value");
HashMap extraContext = new HashMap();
extraContext.put(ActionContext.PARAMETERS, params);
try {
ActionProxy proxy = ActionProxyFactory.getFactory().
createActionProxy("", MockConfigurationProvider.MODEL_DRIVEN_PARAM_TEST, extraContext);
assertEquals(Action.SUCCESS, proxy.execute());
ModelDrivenAction action = (ModelDrivenAction) proxy.getAction();
TestBean model = (TestBean) action.getModel();
String property = System.getProperty("action.security.test");
assertNull(property);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
The framework also provides built-in support for JUnit 3.8 via an
abstract StrutsTestCase, which provides common Struts variables and
setup code.
框架也提供了一個抽象的StrutsTestCase,支持JUnit3.8,這個類提供了常用的Struts變量和啟動代碼。
4,怎樣上傳文件?
You can obtain the MultipartRequestWrapper from the ServletActionContext or by utilizing the fileUpload interceptor. The fileUpload interceptor is preferred.
Ask the ServletActionContext
MultipartRequestWrapper multipartRequest = ((MultipartRequestWrapper)ServletActionContext.getRequest())
The MultipartRequestWrapper provideds access methods such as getFiles, getFile, getContentType, hasErrors, getErrors, and so forth, so that you can process the file uploaded.
Utilize the fileUpload Interceptor
_Preferred_
- Ensure that {{fileUpload }} Interceptor is included in the Action's stack.
The default stack already includes {{fileUpload }}.
- Ensure that the HTML form sets the enctype and specifies on or more file type inputs.
<form name="myForm" enctype="multipart/form-data">
<input type="file" name="myDoc" value="Browse ..." />
<input type="submit" />
</form>
- Ensure that the Action provides one or more fileUpload mutator methods, with names that correspond to name of the file type input.
public void setMyDoc(File myDoc)
public void setMyDocContentType(String contentType)
public void setMyDocFileName(String filename)
- The Action may also provide the corresponding accessor methods.
public File getMyDoc()
public ContentType getMyDocContentType()
public String getMyDocFileName()
Handling multiple files
When multiple files are uploaded by a form, the files are represented by an array.
可以在同一個Form上傳多個文件哦,厲害了
Given:
<form name="myForm" enctype="multipart/form-data">
<input type="file" name="myDoc" value="Browse File A ..." />
<input type="file" name="myDoc" value="Browse File B ..." />
<input type="file" name="myDoc" value="Browse File C ..." />
<input type="submit" />
</form>
The Action class can define file handling methods that accept an array.
public void setMyDoc(File[] myDocs)
public void setMyDocContentType(String[] contentTypes)
public void setMyDocFileName(String[] fileNames)
The uploaded files can be handled by iterating through the appropriate array.
Extra Information
Property | Default |
---|---|
struts.multipart.parser | Commons FileUpload |
struts.multipart.saveDir | javax.servlet.context.tempdir as defined by container |
struts.multipart.maxSize | Approximately 2M |
@see struts.properties
@see org.apache.struts2.dispatcher.FilterDispatcher#doFilter(SerlvetRequest, ServletRepsonse, FilterChain)
@see org.apache.struts2.dispatcher.DispatcherUtil#wrapRequest(HttpServletRequest, SerlvetContext)
@see org.apache.struts2.dispatcher.multipart.MultipartRequestWrapper
@see org.apache.struts2.interceptor.FileUploadInterceptor
5,怎樣處理配置中Result的參數?
Sometimes there is a need to redirect from one action to another, but you do not know the exact url or the destination url requires parameters that are only known in runtime.
有時,需要重定向到另外一個Action,但是你不知道確切的url或者目標url需要的運行時才知道的參數。
Struts 2 gives you easy to use solution for that problem.
Parameters in action result definitions
<struts>
....
<package name="somePackage" namespace="/myNamespace" extends="struts-default">
<action name="myAction" class="com.project.MyAction">
<result name="success" type="redirect-action">otherAction?id=${id}</result>
<result name="back" type="redirect">${redirectURL}</result>
</action>
<action name="otherAction" class="com.project.MyOtherAction">
...
</action>
</package>
....
</struts>
The only requirement is to declare the necessary properties in your action, in this case com.project.MyAction should define properties id and redirectURL with standard accessor methods.
僅僅需要在Action里聲明需要的屬性,本例中:com.project.MyAction 應該定義redirectURL和get函數。
public class MyAction extends ActionSupport {
private int id;
private String redirectURL;
...
public String execute() {
...
if (someCondition) {
this.redirectURL = "/the/target/page.action";
return "back";
}
this.id = 123;
return SUCCESS;
}
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
public String getRedirectURL() { return this.redirectURL; }
public void setRedirectURL(String redirectURL) { this.redirectURL= redirectURL; }
...
}
In the above code if it returns SUCCESS then the browser will be forwarded to
/<app-prefix>/myNamespace/otherAction.action?id=123
6,怎樣訪問Action中的參數?
You can obtain the request parameters by asking the ActionContext or implementing ParameterAware. Implementing ParameterAware is preferred.
你可以通過訪問ActionContext或者實現ParameterAware獲取請求參數。實現ParameterAware是首選的。
Ask the ActionContext
The request parameters are available on the ActionContext instance, which is made available via ThreadLocal.
Map parameters = ActionContext.getContext().getParameters();
Implement ParameterAware
Preferred
- Ensure that servlet-config Interceptor is included in the Action's stack.
The default stack already includes servlet-config.
- Edit the Action so that it implements the ParameterAware interface.
- The ParameterAware interface expects a setParameters method. You may wish to include a companion getParameters method.
- At runtime, call getParameters to obtain a Map representing the request parameters.
Map parameters = this.getParameters();
![]() |
When the servlet-config Interceptor sees that an Action implements ParameterAware, it passes a Map of the request parameters to the Action's setParameters method. |
![]() |
To unit test a ParameterAware Action, create your own Map with the pertinent request parameters and call setParameters as part of the test's setUp method. |
@see struts-default.xml
@see org.apache.struts.action2.interceptor.ParameterAware
@see org.apache.struts.action2.interceptor.Servlet Config Interceptor
You can obtain the UserPrincipal and other security details by going through the request or implementing PrincipalAware. Implementing PrincipalAware is preferred.
Go Through the Request
First obtain the HttpServletRequest and then obtain the security Principal.
HttpServletRequest request = ServletActionContext.getRequest();
String authType = request.getAuthType(); // http or https
String user = request.getRemoteUser(); // the user principal (in string)
Principalprincipal = request.getUserPrincipal(); // get a Principal object
bool isAuth = request.isUserInRole("patrick");
Implement PrincipalAware
Preferred
- Ensure that servlet-config Interceptor is included in the Action's stack.
The default stack already includes servlet-config.
- Edit the Action so that it implements the PrincipalAware interface.
- The PrincipalAware interface expects a setPrincipalProxy(PrincipalProxy) method. You may wish to include a companion getPrincipalProxy method.
- At runtime, use the PrincipalProxy reference to invoke methods such as isUserInRole, getUserPrincipal(), getRemoteUser(), isRequestSecure(), and so forth.
@see org.apache.struts.action2.interceptor.PrincipalProxy
@see org.apache.struts.action2.interceptor.PrincipalAware
@see org.apache.struts.action2.interceptor.ServletConfigInterceptor
8,怎樣訪問JSTL?
![]() |
About JSTL The JavaServer Pages Standard Tag Library ![]() |
JSTL integration is built into the framework - there are no steps
required to enable it. Simply refer to your JSTL expressions just as
you would with a normal SAF JSP tag, such as the property tag.
JSTL被整合到框架里面,不需要做任何工作。GOOD!
![]() |
Javadoc: (org.apache.struts.action2.dispatcher.StrutsRequestWrapper) All Struts requests are wrapped with this class, which provides simple JSTL accessibility. This is because JSTL works with request attributes, so this class delegates to the value stack except for a few cases where required to prevent infinite loops. Namely, we don't let any attribute name with "#" in it delegate out to the value stack, as it could potentially cause an infinite loop. For example, an infinite loop would take place if you called: request.getAttribute("#attr.foo"). |
posted on 2007-04-27 14:01 MingIsMe 閱讀(1282) 評論(0) 編輯 收藏 所屬分類: Struts2學習