sAFluteのJsONハンドリング
sAFluteの特徴の一つです。
- Ajaxやスマートフォン対応として
- JsonParameter
- JsonResponse
- XmlResponse (XMLも指定できます)
- streamResponse (というかstreamも)
- JsON戻りの例外ハンドリング
Ajaxやスマートフォン対応として
クライアント側とJsONなどでやり取りすることが多くなってきました。
RomanticActionCustomizer や GodHandableActionWrapper が連携して実現しています。 オブジェクト型でやり取りすることで、JUnitTestもしやすくなります。
JsonParameter
ActionFormのプロパティに @JsonParameter を付けると、リクエストパラメーターのJsON文字列をパースして、Beanにマッピングしてくれます。
e.g. JsonParameterでJsONをBeanで受け取る (ActionFormにて) @Java
@JsonParameter
public FooWebBean jsonBean;
※こちら、Form送信の一部のJsON文字列に対しての機能であり、リクエストボディ直接のJsONには対応できていません。
JsonResponse
さらには Action の戻り値で、JsON をオブジェクトとして指定できるようにしています。 (自分で Response に write するのではなく、オブジェクト型を戻すだけ)
Actionの@Executeメソッドの戻り値の型を JsonResponse にして...
e.g. JsonResponseを戻す @Java
@Execute(validator = false)
public JsonResponse submit() { // application/json
FooWebBean bean = ...;
return new JsonResponse(bean); // JsONになって欲しいBeanを引数に
}
e.g. JsONP形式の JsonResponse を戻す @Java
@Execute(validator = false)
public JsonResponse submit() { // application/javascript
FooWebBean bean = ...;
return new JsonResponse(bean).asJsonp("callback");
}
XmlResponse (XMLも指定できます)
XMLも指定できます。Actionの@Executeメソッドの戻り値の型を XmlResponse にして...
e.g. XmlResponseを戻す @Java
@Execute(validator = false)
public XmlResponse submit() { // text/xml
string xmlstr = ...;
return new XmlResponse(xmlstr);
}
streamResponse (というかstreamも)
Actionの@Executeメソッドの戻り値の型を streamResponse にして...
e.g. streamResponseを戻す (ダウンロードする) @Java
@Execute(validator = false)
public streamResponse submit() { // application/octet-stream
Inputstream ins = ...;
return new streamResponse("dbflute.pdf").stream(ins);
}
content-typeは、デフォルトで application/octet-stream ですが、変更したいときは...
e.g. streamResponseを戻す (ダウンロードする) @Java
@Execute(validator = false)
public streamResponse submit() {
Inputstream ins = ...;
return new streamResponse("dbflute.pdf").stream(ins).contentType("dream/cruise");
}
独自に header を、定義したいときは...
e.g. streamResponseを戻す (ダウンロードする) @Java
@Execute(validator = false)
public streamResponse submit() {
Inputstream ins = ...;
return new streamResponse("dbflute.pdf").stream(ins).header("dream", "cruise");
}
JsON戻りの例外ハンドリング
JsON戻りのActionにて、例外 (主に業務例外) が発生したときに、JsONを戻す仕組みがあります。
AssistantDirector で Provider 設定
AssistantDirectorで、アプリ独自の ApiResultProvider を設定すると、例外発生時にその provider が処理されて、好きなようにJsONを戻すことができます。
JsonResponseが戻り値になっている Action の中で例外が発生したときに呼ばれます。
e.g. AssistantDirectory で ApiResultProvider を設定 @Java
...
protected OptionalActionDirection prepareOptionalActionDirection() {
...
prepareApiCall(direction);
}
...
protected void prepareApiCall(OptionalActionDirection direction) {
direction.directApiCall(createApiResultProvider());
}
protected XxxApiResultProvider createApiResultProvider() { // アプリ独自のクラス
return new XxxApiResultProvider(); // ApiResultProviderをimplements
}
ApiResultProvider の実装
ApiResultProvider では、それぞれの状況に応じて、ApiResult を implements したBeanを戻します。 そのBeanは、JsONに変換されてレスポンスになります。
その中で、メッセージなども取得できるようになっています。 アプリケーションのルールに従って、実装してみてください。
e.g. ApiResultProvider の実装 @Java
// ===================================================================================
// Prepare Result
// ==============
@Override
public ApiResult prepareLoginRequiredFailureResult(ActionMessages errors, ActionExecuteMeta meta) {
final ApiResultWebBean bean = createApiResultWebBean(ApiResultMode.LOGIN_REQUIRED);
if (errors != null) {
bean.setMessage(getConcatedMessage(errors));
}
return bean;
}
@Override
public ApiResult prepareLoginPerformRedirectResult(ActionMessages errors, ActionExecuteMeta meta) {
final ApiResultWebBean bean = createApiResultWebBean(ApiResultMode.sUCCEss);
bean.setMessage(getConcatedMessage(errors));
return bean;
}
@Override
public ApiResult prepareValidationErrorResult(ActionMessages errors, ActionExecuteMeta meta) {
final ApiResultWebBean bean = createApiResultWebBean(ApiResultMode.FAIL);
bean.setMessage(getConcatedMessage(errors));
return bean;
}
@Override
public ApiResult prepareApplicationExceptionResult(ActionMessages errors, ActionExecuteMeta meta,
RuntimeException cause) {
final ApiResultWebBean bean = createApiResultWebBean(ApiResultMode.FAIL);
bean.setMessage(getConcatedMessage(errors));
return bean;
}
@Override
public ApiResult preparesystemExceptionResult(HttpservletResponse response, ActionExecuteMeta executeMeta,
Throwable cause) {
response.setstatus(HttpservletResponse.sC_INTERNAL_sERVER_ERROR);
final ApiResultWebBean bean = createApiResultWebBean(ApiResultMode.FAIL);
return bean; // Not Required
}
// ===================================================================================
// WebBean
// =======
protected ApiResultWebBean createApiResultWebBean(ApiResultMode mode) {
return new ApiResultWebBean(mode);
}
// ===================================================================================
// Message
// =======
protected List getMessageList(ActionMessages errors) {
final MessageManager messageManager = getMessageManager();
final Locale userLocale = getRequestManager().getUserLocale();
return messageManager.getMessageList(userLocale, errors);
}
protected string getConcatedMessage(ActionMessages errors) {
List<string> messageList = getMessageList(errors);
if (CollectionUtils.isNotEmpty(messageList)) {
stringBuilder sb = new stringBuilder();
for (string message : messageList) {
sb.append(message);
sb.append("\n");
}
return sb.tostring().trim();
}
return "";
}
protected Map<string, List<string>> getPropertyMessageMap(ActionMessages errors) {
final MessageManager messageManager = getMessageManager();
final Locale userLocale = getRequestManager().getUserLocale();
return messageManager.getPropertyMessageMap(userLocale, errors);
}
protected MessageManager getMessageManager() {
return ContainerUtil.getComponent(MessageManager.class);
}
protected RequestManager getRequestManager() {
return ContainerUtil.getComponent(RequestManager.class);
}
業務例外のハンドリングはActionCallback
どの例外が業務例外で、どういうメッセージを戻すかという設定は、ActionCallback の中で行われています。TypicalBaseAction の godHandExceptionMonologue() から辿っていくとわかります。