Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To validate a Struts 2 form without reloading the page, run the normal server-side validation stack through the JSON plugin’s jsonValidation interceptor, then have JavaScript display the returned errors. Send struts.enableJSONValidation=true with the request; use struts.validateOnly=true if the action should stop after validation.

How Struts 2 Ajax validation works

Ajax validation is still server-side validation. The browser sends the form values to Struts, where the validation stack—including applicable visitor validators and the action’s validate() method—runs. Struts returns validation results as JSON, and your JavaScript updates the existing page.

The JSON plugin’s jsonValidation interceptor supplies that response. It must run after validation has populated the action’s errors. The documented jsonValidationWorkflowStack combines the needed behavior; alternatively, configure a stack with validation before jsonValidation. Normal input and success results remain useful for requests that are not handled through Ajax.

Configure the action and choose whether it runs

First define the rules using XML validation files, annotations, or action code. Struts’ validation interceptor runs those rules and creates field and action errors; the workflow interceptor checks for errors and normally returns the input result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Configure the action to use jsonValidationWorkflowStack, or use a custom stack that runs validation before jsonValidation.
  2. Include struts.enableJSONValidation=true in the Ajax request to activate the JSON validation path.
  3. Set struts.validateOnly=true when the request should validate and stop before the action executes. If the action should run after successful validation, set it to false.
  4. If successful action execution should send the browser to a location, configure jsonActionRedirect; the JSON response can then contain a location value.

With validate-only enabled, a successful response is an empty JSON object, {}. A validation failure is returned with HTTP 400 and JSON errors.

Understand the JSON response

A failed response separates errors that apply to the request or action from errors tied to a particular field:

{
  "errors": ["Global error"],
  "fieldErrors": {
    "email": ["Email is invalid"]
  }
}

Use errors for messages that do not belong beside one control, and map each fieldErrors key to the corresponding form control. When the action is model-driven, the interceptor removes the model. prefix from returned field names.

The request parameter names can be changed in stack configuration with validateJsonParam, validateOnlyParam, and noEncodingSetParam. The Struts documentation identifies those overrides as available since version 2.5.9.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep error targets in the page before validation runs

JavaScript cannot insert a message into an error container that does not exist in the DOM. Keep the field and global error targets in the page from its initial render, including when there are no server-side errors yet. Struts’ documented example uses the ajaxErrorContainers theme for this reason.

For a custom renderer, the following markup uses a simple convention: each field’s message target has a data-error-for value matching the control’s name, and the page has one global error target.

<div data-global-errors aria-live="polite"></div>

<label for="email">Email</label>
<input id="email" name="email" type="email">
<div data-error-for="email" aria-live="polite"></div>

Submit asynchronously and render field errors

This vanilla JavaScript example sends a URL-encoded form, clears stale messages, and renders the returned errors as text. Its data-error-for and data-global-errors attributes are application markup conventions, not Struts-specific names.

const form = document.querySelector("#signup-form");

form.addEventListener("submit", async (event) => {
  event.preventDefault();

  const globalErrors = document.querySelector("[data-global-errors]");
  globalErrors.replaceChildren();
  form.querySelectorAll("[data-error-for]").forEach((target) => {
    target.replaceChildren();
  });

  const params = new URLSearchParams(new FormData(form));
  params.set("struts.enableJSONValidation", "true");
  params.set("struts.validateOnly", "true");

  let response;
  try {
    response = await fetch(form.action, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" },
      body: params
    });
  } catch (error) {
    globalErrors.textContent = "Could not contact the server. Check your connection and try again.";
    return;
  }

  let result;
  try {
    result = await response.json();
  } catch (error) {
    globalErrors.textContent = "The server returned an unreadable response.";
    return;
  }

  if (!response.ok && response.status !== 400) {
    globalErrors.textContent = "The request could not be completed. Try again later.";
    return;
  }

  (result.errors || []).forEach((message) => {
    const item = document.createElement("p");
    item.textContent = message;
    globalErrors.append(item);
  });

  Object.entries(result.fieldErrors || {}).forEach(([fieldName, messages]) => {
    const target = Array.from(form.querySelectorAll("[data-error-for]"))
      .find((element) => element.dataset.errorFor === fieldName);
    if (!target) return;

    messages.forEach((message) => {
      const item = document.createElement("p");
      item.textContent = message;
      target.append(item);
    });
  });

  if (response.status === 400 && !result.errors?.length && !Object.keys(result.fieldErrors || {}).length) {
    globalErrors.textContent = "The form could not be validated.";
  }
});

Using textContent treats messages as text rather than markup. The example treats HTTP 400 as the documented validation-failure status, handles other HTTP failures separately, and reports network or unreadable-response problems without presenting them as field validation errors.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose the right kind of validation

Struts supports validators including required, required-string, integer, date, email, URL, string length, regex, expression, and visitor validation. Prefer a field-specific validator when the error belongs to a particular control; its message can then appear beside that control.

Approach Where rules run How the user gets feedback Key trade-off
Browser-native constraints In the browser Typically during browser validation before submission Useful for quick feedback, but not a replacement for server-side rules.
Struts Ajax validation On the server, through the Struts validation stack JavaScript renders the JSON errors without a full-page reload Requires an Ajax request, persistent message targets, and client-side response handling.
Regular form submission On the server, through the Struts validation stack The action returns its normal result, commonly the input result when errors exist Uses the regular page flow rather than updating the current form asynchronously.

Browser constraints and Struts validation can complement one another: the browser can catch simple input problems early, while server-side validation remains authoritative. The Struts documentation says version 7.4.0 added HTML5 constraint attributes through the html5 theme when struts.ui.html5.constraints=true. That browser feature is distinct from server-backed Ajax validation. The same documentation says older pure-JavaScript client validation was deprecated in 7.4.0 and removed in 8.0.0; do not confuse that legacy feature or the Dojo-based Ajax theme with the JSON validation flow described here.

Remember what Ajax changes—and what it does not

An Ajax call is still an ordinary HTTP request to Struts. The difference is that JavaScript consumes the response rather than navigating directly to a rendered page. Struts can return JSON, HTML, XML, text, or another script; the JSON plugin is the documented route for this JSON validation response.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.