Help
+
+
+
+ Struts2 Showcase - Validation - AJAX Form Submit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Action Errors Will Appear Here
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/showcase/src/main/webapp/WEB-INF/validation/ajaxFormSubmitSuccess.jsp b/apps/showcase/src/main/webapp/WEB-INF/validation/ajaxFormSubmitSuccess.jsp
new file mode 100644
index 000000000..f8a07312b
--- /dev/null
+++ b/apps/showcase/src/main/webapp/WEB-INF/validation/ajaxFormSubmitSuccess.jsp
@@ -0,0 +1,17 @@
+<%@taglib prefix="s" uri="/struts-tags" %>
+
+
+ Struts2 Showcase - Validation - Success Field Validators Example
+
+
+
+
+
+
+
+Form has been submitted.
+
+
+
diff --git a/apps/showcase/src/main/webapp/WEB-INF/validation/index.jsp b/apps/showcase/src/main/webapp/WEB-INF/validation/index.jsp
index 96a331f5a..00c157948 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/validation/index.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/validation/index.jsp
@@ -31,6 +31,7 @@
+
- Field Validators
@@ -42,6 +43,7 @@
- Validation (client)
- Validation (client using css_xhtml theme)
- Visitor Validator
+ - AJAX Form Submit
diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONActionRedirectResult.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONActionRedirectResult.java
new file mode 100644
index 000000000..f9b28b248
--- /dev/null
+++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONActionRedirectResult.java
@@ -0,0 +1,71 @@
+package org.apache.struts2.json;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.dispatcher.ServletActionRedirectResult;
+
+/**
+ * Specialized form of {@link ServletActionRedirectResult} which takes care of
+ * situation that browser has a JS/AJAX context, there are no validation errors
+ * and action is executed. In this case a http redirect is harmful as browsers
+ * don't pass them to JS handlers. So this result produces a JSON response
+ * containing redirect data.
+ *
+ *
+ * To be used along with {@link JSONValidationInterceptor}.
+ *
+ *
+ * Response JSON looks like this:
+ *
{"location": "$redirect url$"}
+ *
+ *
+ */
+public class JSONActionRedirectResult extends ServletActionRedirectResult {
+
+ private static final long serialVersionUID = 3107276294073879542L;
+
+ @Override
+ protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException {
+ if (sendJsonInsteadOfRedirect()) {
+ printJson(response, finalLocation);
+ } else {
+ super.sendRedirect(response, finalLocation);
+ }
+ }
+
+ /**
+ * If browser has called action in a JS/AJAX context we cannot send a
+ * redirect as response.
+ *
+ * @return true if a JSON response shall be generated, false if a redirect
+ * shall be sent.
+ */
+ static boolean sendJsonInsteadOfRedirect() {
+ HttpServletRequest request = ServletActionContext.getRequest();
+ return isJsonEnabled(request) && !isValidateOnly(request);
+ }
+
+ static void printJson(HttpServletResponse response, String finalLocation) throws IOException {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType("application/json");
+ response.setHeader("Location", finalLocation);
+ PrintWriter writer = response.getWriter();
+ writer.write("{\"location\": \"");
+ writer.write(finalLocation);
+ writer.write("\"}");
+ writer.close();
+ }
+
+ private static boolean isJsonEnabled(HttpServletRequest request) {
+ return "true".equals(request.getParameter(JSONValidationInterceptor.VALIDATE_JSON_PARAM));
+ }
+
+ private static boolean isValidateOnly(HttpServletRequest request) {
+ return "true".equals(request.getParameter(JSONValidationInterceptor.VALIDATE_ONLY_PARAM));
+ }
+}
diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONValidationInterceptor.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONValidationInterceptor.java
index 819420576..1c7fd5433 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/JSONValidationInterceptor.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONValidationInterceptor.java
@@ -61,6 +61,8 @@ import java.util.Map;
* If the request has a parameter 'struts.validateOnly' execution will return after
* validation (action won't be executed).
*
+ * If 'struts.validateOnly' is set to false you may want to use {@link JSONActionRedirectResult}.
+ *
* A request parameter named 'struts.enableJSONValidation' must be set to 'true' to
* use this interceptor
*
@@ -72,8 +74,8 @@ public class JSONValidationInterceptor extends MethodFilterInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(JSONValidationInterceptor.class);
- private static final String VALIDATE_ONLY_PARAM = "struts.validateOnly";
- private static final String VALIDATE_JSON_PARAM = "struts.enableJSONValidation";
+ static final String VALIDATE_ONLY_PARAM = "struts.validateOnly";
+ static final String VALIDATE_JSON_PARAM = "struts.enableJSONValidation";
private static final String NO_ENCODING_SET_PARAM = "struts.JSONValidation.no.encoding";
private static final String DEFAULT_ENCODING = "UTF-8";
diff --git a/plugins/json/src/main/resources/struts-plugin.xml b/plugins/json/src/main/resources/struts-plugin.xml
index 5abfbea2c..58c06c789 100644
--- a/plugins/json/src/main/resources/struts-plugin.xml
+++ b/plugins/json/src/main/resources/struts-plugin.xml
@@ -9,6 +9,7 @@
+
diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java
new file mode 100644
index 000000000..c98e41a17
--- /dev/null
+++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java
@@ -0,0 +1,105 @@
+package org.apache.struts2.json;
+
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.StrutsTestCase;
+import org.apache.struts2.dispatcher.mapper.DefaultActionMapper;
+import org.apache.struts2.views.util.DefaultUrlHelper;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.mock.MockActionInvocation;
+import com.opensymphony.xwork2.mock.MockActionProxy;
+import com.opensymphony.xwork2.util.ValueStack;
+
+public class JSONActionRedirectResultTest extends StrutsTestCase {
+
+ MockActionInvocation invocation;
+ MockHttpServletResponse response;
+ MockServletContext servletContext;
+ ActionContext context;
+ ValueStack stack;
+ MockHttpServletRequest request;
+
+ public void testNormalRedirect() throws Exception {
+ JSONActionRedirectResult result = new JSONActionRedirectResult();
+ result.setActionName("targetAction");
+ result.setActionMapper(new DefaultActionMapper());
+ result.setUrlHelper(new DefaultUrlHelper());
+
+ Object action = new Object();
+ stack.push(action);
+
+ this.invocation.setAction(action);
+ result.execute(this.invocation);
+
+ String content = response.getContentAsString();
+ assertEquals("", content);
+ String location = response.getHeader("Location");
+ assertEquals("/targetAction.action", location);
+ assertEquals(302, response.getStatus());
+ }
+
+ public void testJsonRedirect() throws Exception {
+ JSONActionRedirectResult result = new JSONActionRedirectResult();
+ result.setActionName("targetAction");
+ result.setActionMapper(new DefaultActionMapper());
+ result.setUrlHelper(new DefaultUrlHelper());
+
+ request.setParameter("struts.enableJSONValidation", "true");
+ request.setParameter("struts.validateOnly", "false");
+
+ Object action = new Object();
+ stack.push(action);
+
+ this.invocation.setAction(action);
+ result.execute(this.invocation);
+
+ String content = response.getContentAsString();
+ assertEquals("{\"location\": \"/targetAction.action\"}", content);
+ assertEquals(200, response.getStatus());
+ }
+
+ public void testValidateOnlyFalse() throws Exception {
+ JSONActionRedirectResult result = new JSONActionRedirectResult();
+ result.setActionName("targetAction");
+ result.setActionMapper(new DefaultActionMapper());
+ result.setUrlHelper(new DefaultUrlHelper());
+
+ request.setParameter("struts.enableJSONValidation", "true");
+ request.setParameter("struts.validateOnly", "true");
+
+ Object action = new Object();
+ stack.push(action);
+
+ this.invocation.setAction(action);
+ result.execute(this.invocation);
+
+ String content = response.getContentAsString();
+ assertEquals("", content);
+ String location = response.getHeader("Location");
+ assertEquals("/targetAction.action", location);
+ assertEquals(302, response.getStatus());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.response = new MockHttpServletResponse();
+ this.request = new MockHttpServletRequest();
+ this.context = ActionContext.getContext();
+ this.context.put(StrutsStatics.HTTP_RESPONSE, this.response);
+ this.context.put(StrutsStatics.HTTP_REQUEST, this.request);
+ this.stack = context.getValueStack();
+ this.servletContext = new MockServletContext();
+ this.context.put(StrutsStatics.SERVLET_CONTEXT, this.servletContext);
+ this.invocation = new MockActionInvocation();
+ this.invocation.setInvocationContext(this.context);
+ this.invocation.setStack(this.stack);
+ MockActionProxy mockActionProxy = new MockActionProxy();
+ mockActionProxy.setConfig(new ActionConfig.Builder(null, null, null).build());
+ this.invocation.setProxy(mockActionProxy);
+ }
+}