package sample.controller; import java.io.IOException; import java.net.URLEncoder; import java.util.*; import java.util.function.Supplier; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.http.*; import org.springframework.web.multipart.MultipartFile; import lombok.Setter; import sample.ValidationException; import sample.context.ResourceBundleHandler; import sample.context.Timestamper; import sample.context.actor.ActorSession; import sample.context.report.ReportFile; /** * UIコントローラの基底クラス。 */ @Setter public class ControllerSupport { @Autowired private MessageSource msg; @Autowired private ResourceBundleHandler label; @Autowired private Timestamper time; @Autowired private ActorSession session; /** i18nメッセージ変換を行います。 */ protected String msg(String message) { return msg(message, session.actor().getLocale()); } protected String msg(String message, final Locale locale) { return msg.getMessage(message, new String[0], locale); } /** * リソースファイル([basename].properties)内のキー/値のMap情報を返します。 * <p>API呼び出し側でi18n対応を行いたい時などに利用してください。 */ protected Map<String, String> labels(String basename) { return labels(basename, session.actor().getLocale()); } protected Map<String, String> labels(String basename, final Locale locale) { return label.labels(basename, locale); } /** メッセージリソースアクセサを返します。 */ protected MessageSource msgResource() { return msg; } /** 日時ユーティリティを返します。 */ protected Timestamper time() { return time; } /** * 指定したキー/値をMapに変換します。 * get等でnullを返す可能性があるときはこのメソッドでMap化してから返すようにしてください。 * ※nullはJSONバインドされないため、クライアント側でStatusが200にもかかわらず例外扱いされる可能性があります。 */ protected <T> Map<String, T> objectToMap(String key, final T t) { Map<String, T> ret = new HashMap<>(); ret.put(key, t); return ret; } protected <T> Map<String, T> objectToMap(final T t) { return objectToMap("result", t); } /** 戻り値を生成して返します。(戻り値がプリミティブまたはnullを許容する時はこちらを利用してください) */ protected <T> ResponseEntity<T> result(Supplier<T> command) { return ResponseEntity.status(HttpStatus.OK).body(command.get()); } protected ResponseEntity<Void> resultEmpty(Runnable command) { command.run(); return ResponseEntity.status(HttpStatus.OK).build(); } /** ファイルアップロード情報(MultipartFile)をReportFileへ変換します。 */ protected ReportFile uploadFile(final MultipartFile file) { return uploadFile(file, (String[]) null); } /** * ファイルアップロード情報(MultipartFile)をReportFileへ変換します。 * <p>acceptExtensionsに許容するファイル拡張子(小文字統一)を設定してください。 */ protected ReportFile uploadFile(final MultipartFile file, final String... acceptExtensions) { String fname = StringUtils.lowerCase(file.getOriginalFilename()); if (acceptExtensions != null && !FilenameUtils.isExtension(fname, acceptExtensions)) { throw new ValidationException("file", "アップロードファイルには[{0}]を指定してください", new String[] { StringUtils.join(acceptExtensions) }); } try { return new ReportFile(file.getOriginalFilename(), file.getBytes()); } catch (IOException e) { throw new ValidationException("file", "アップロードファイルの解析に失敗しました"); } } /** * ファイルダウンロード設定を行います。 * <p>利用する際は戻り値をvoidで定義するようにしてください。 */ protected void exportFile(final HttpServletResponse res, final ReportFile file) { exportFile(res, file, MediaType.APPLICATION_OCTET_STREAM_VALUE); } protected void exportFile(final HttpServletResponse res, final ReportFile file, final String contentType) { String filename; try { filename = URLEncoder.encode(file.getName(), "UTF-8").replace("+", "%20"); } catch (Exception e) { throw new ValidationException("ファイル名が不正です"); } res.setContentLength(file.size()); res.setContentType(contentType); res.setHeader("Content-Disposition", "attachment; filename=" + filename); try { IOUtils.write(file.getData(), res.getOutputStream()); } catch (IOException e) { throw new ValidationException("ファイル出力に失敗しました"); } } }