SaStrutsのvalidationをJSON形式で返す
入力チェック等のValidationの処理をSaStruts側(アノテーションとか)で処理して結果を非同期で返す(JSON)処理を作ってみました。
処理の流れ
・サーバーサイドにリクエストが送られてくる。
・SaStruts側でValidationチェックを行う。
・処理結果をActionWrapperクラスを継承したクラスでJSON形式にフォーマットしてレスポンスを返す。
・クライアントサイドでレスポンスを受け取りJSON形式からエラーメッセージのhtmlを生成して表示。
修正箇所(サーバーサイド)
SaStruts側の修正部分は、ActionWrapperクラスとS2RequestProcessorを修正というか
継承して、変更を加えたい箇所のメソッドをオーバーライドします。
あと、非同期で返す処理だということを認識させるためのアノテーションを作成と
S2RequestProcessorから新しく作成するRequestProcessorに変更するので
struts-config.xmlを修正します。
アノテーションクラス
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * <pre> * Ajax * 非同期通信でvalidationエラーを返すためのアノテーション * </pre> * @version 1.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface Ajax { String input = "ajax"; }
ActionWrapperを継承したクラス。
import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; import net.arnx.jsonic.JSON; import net.arnx.jsonic.JSONException; import org.apache.struts.Globals; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.util.MessageResources; import org.apache.struts.validator.Resources; import org.seasar.framework.container.deployer.InstanceDefFactory; import org.seasar.framework.util.MethodUtil; import org.seasar.struts.action.ActionWrapper; import org.seasar.struts.config.S2ActionMapping; import org.seasar.struts.config.S2ExecuteConfig; import org.seasar.struts.config.S2ValidationConfig; import org.seasar.struts.util.ActionMessagesUtil; import org.seasar.struts.util.RequestUtil; import org.seasar.struts.util.ResponseUtil; /** */ public class SaStrutsActionWrapper extends ActionWrapper { /** * コンストラクタ * @param actionMapping */ public SaStrutsActionWrapper(S2ActionMapping actionMapping) { super(actionMapping); } @Override protected ActionForward execute(HttpServletRequest request, S2ExecuteConfig executeConfig) { ActionMessages errors = new ActionMessages(); List validationConfigs = executeConfig.getValidationConfigs(); if (validationConfigs != null) { for (S2ValidationConfig cfg : validationConfigs) { if (cfg.isValidator()) { ActionMessages errors2 = validateUsingValidator(request, executeConfig); if (errors2 != null && !errors2.isEmpty()) { errors.add(errors2); if (executeConfig.isStopOnValidationError()) { return processErrors(errors, request, executeConfig); } } } else { Object target = actionForm; if (cfg.getValidateMethod().getDeclaringClass() .isAssignableFrom( actionMapping.getComponentDef() .getComponentClass())) { target = action; } ActionMessages errors2 = (ActionMessages) MethodUtil .invoke(cfg.getValidateMethod(), target, null); if (errors2 != null && !errors2.isEmpty()) { errors.add(errors2); if (executeConfig.isStopOnValidationError()) { // @executeのinputがajaxだった場合 if (executeConfig.getInput().equals("ajax")) { // JSON形式のERROR MSGを作成 createJsonErrMsg(errors, request); return null; } return processErrors(errors, request, executeConfig); } } } } } if (!errors.isEmpty()) { return processErrors(errors, request, executeConfig); } String next = (String) MethodUtil.invoke(executeConfig.getMethod(), action, null); if (executeConfig.isRemoveActionForm() && !ActionMessagesUtil.hasErrors(request)) { if (actionMapping.getActionFormComponentDef().getInstanceDef() .equals(InstanceDefFactory.SESSION)) { RequestUtil.getRequest().getSession().removeAttribute( actionMapping.getActionFormComponentDef() .getComponentName()); } else { RequestUtil.getRequest().removeAttribute( actionMapping.getActionFormComponentDef() .getComponentName()); } RequestUtil.getRequest().removeAttribute( actionMapping.getAttribute()); } else { if (ActionMessagesUtil.hasErrors(request)) { // @executeのinputがajaxだった場合 if (executeConfig.getInput() != null && executeConfig.getInput().equals("ajax")) { errors.add((ActionMessages) request.getAttribute(Globals.ERROR_KEY)); // JSON形式のERROR MSGを作成 createJsonErrMsg(errors, request); return null; } } } boolean redirect = executeConfig.isRedirect(); if (redirect && ActionMessagesUtil.hasErrors(request)) { redirect = false; } return actionMapping.createForward(next, redirect); } @Override protected ActionForward processErrors(ActionMessages errors, HttpServletRequest request, S2ExecuteConfig executeConfig) { // @executeのinputがajaxだった場合 if (executeConfig.getInput().equals("ajax")) { // JSON形式のERROR MSGを作成 createJsonErrMsg(errors, request); return null; } return super.processErrors(errors, request, executeConfig); } /** * JSON形式のエラーメッセージを生成します。 * @param errors * @param request * @throws JSONException */ @SuppressWarnings("unchecked") private void createJsonErrMsg(ActionMessages errors, HttpServletRequest request) throws JSONException { // JSON形式で返す Map map = new HashMap(); map.put("status", "validate"); List<Object> list = new ArrayList<Object>(); for (Iterator<String> it = errors.properties(); it.hasNext();) { Map<String, String> err = new HashMap<String, String>(); String key = it.next(); err.put("name", key); for (Iterator<ActionMessage> ite = errors.get(key); ite.hasNext();) { ActionMessage error = ite.next(); System.out.println(error); MessageResources mResouces = Resources.getMessageResources(request); String msg = mResouces.getMessage((Locale)null, error.getKey(), error.getValues()); System.out.println(msg); err.put("val", msg); } list.add(err); } map.put("errors", list); ResponseUtil.write(JSON.encode(map), "application/json"); } }
S2RequestProcessorを継承したクラス
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionMapping; import org.seasar.struts.action.S2RequestProcessor; import org.seasar.struts.config.S2ActionMapping; /** * <pre> * SaStrutsRequestProcessor * * </pre> * @version 1.0.0 */ public class SaStrutsRequestProcessor extends S2RequestProcessor { /* * (非 Javadoc) * @see org.seasar.struts.action.S2RequestProcessor#processActionCreate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.struts.action.ActionMapping) */ @Override protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException { Action action = null; try { action = new SaStrutsActionWrapper(((S2ActionMapping) mapping)); } catch (Exception e) { log.error(getInternal().getMessage("actionCreate", mapping.getPath()), e); response .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("actionCreate", mapping.getPath())); return null; } action.setServlet(servlet); return action; } }
<controller maxFileSize="1024K" bufferSize="1024" processorClass="xxx.xxx.xxxx.wrapper.SaStrutsRequestProcessor" multipartClass="org.seasar.struts.upload.S2MultipartRequestHandler"/>
Actionクラス
(validatorをtrueにして、inputをさっき作ったAjaxアノテーションクラスで指定
/** * ログイン処理アクション * @return */ @Execute(validator = true, input = Ajax.input) public String index(){ }
とりあえずjava側の修正は終了。
あとは、改めて。。。