Pocket

こんにちは。kikuchiです。

タイトル通りですが、例えばこんな場面を想定します。

・表示するデータをどこかから取得
・改行が含まれている
・XSS対策でエスケープ処理を行う

jstlのc:outだと、エスケープと同時に出力するので改行をbrに置換できず、先に置換するとエスケープされてしまいます。
サーバ側でエスケープ処理を行い、改行をbrタグに置換した上で画面へ渡す場合、画面側がサーバの処理を意識する必要があります。(c:out等で出力すると結局brタグもエスケープされる)
fn:splitで改行毎に分割し、個々にc:outしつつbrを入れていく…というのも考えられますが、下記のような問題(仕様)もありちょっとかっこ悪いです。
[Java]JSTLで改行でのsplit()ができない問題

そこで、jstlのc:outのような感覚で、簡易に使えるカスタムタグがあると便利かなーと思い、作成してみました。
処理内容についてはコード内のコメントを参照してください。
ここでは、jstlライブラリ内のOutSupportを使用しています。

  • NewLineToBRTag.java
  • package jp.co.opentone.tag;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.tagext.TagSupport;
    
    import org.apache.taglibs.standard.tag.common.core.OutSupport;
    
    /**
     * 改行をbrタグにして出力する。
     */
    public class NewLineToBRTag extends TagSupport {
    
        // 対象文字列 必須
        private String value;
    
        // エスケープ実施有無 非必須
        private boolean escapeXml = true;
    
        /**
         * doStartTag
         */
        @Override
        public int doStartTag() throws JspException {
    
            try {
                JspWriter writer = pageContext.getOut();
                // 改行コードを全てLFに変換し、LFでsplit
                for (String str : value.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n")) {
                    // OutSupport.outで出力(escapeXml=true でエスケープ)
                    OutSupport.out(pageContext, escapeXml, str);
                    // brタグを出力
                    writer.write("<br />");
                }
            } catch (IOException e) {
                throw new JspException(e.getMessage());
            }
    
            return SKIP_BODY;
        }
    
        /**
         * doEndTag
         */
        @Override
        public int doEndTag() throws JspException {
            return EVAL_PAGE;
        }
    
        /**
         * valueを設定します。
         * @param value value
         */
        public void setValue(String value) {
            this.value = value;
        }
    
        /**
         * escapeXmlを設定します。
         * @param escapeXml escapeXml
         */
        public void setEscapeXml(boolean escapeXml) {
            this.escapeXml = escapeXml;
        }
    }
    
  • NewLineToBRTag.tld
  • <?xml version="1.0" encoding="UTF-8" ?>
    
    <taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">
    
        <tlib-version>1.0</tlib-version>
        <short-name>br</short-name>
    
        <tag>
            <name>newLineToBr</name>
            <tag-class>jp.co.opentone.tag.NewLineToBRTag</tag-class>
            <body-content>empty</body-content>
            <attribute>
                <name>value</name>
                <required>true</required>
                <rtexprvalue>true</rtexprvalue>
            </attribute>
            <attribute>
                <name>escapeXml</name>
                <required>false</required>
                <rtexprvalue>true</rtexprvalue>
            </attribute>
        </tag>
    </taglib>
    

    使えるようにするため、web.xmlに追加します。

  • web.xml
  •     <jsp-config>
            <taglib>
                <taglib-uri>http://opentone.co.jp/sample/tag/NewLineToBR</taglib-uri>
                <taglib-location>/WEB-INF/tags/NewLineToBRTag.tld</taglib-location>
            </taglib>
        </jsp-config>
    



    さっそく画面表示して試してみます。
    6270_001

    ちゃんと出来てますね。
    テスト用に書いたコードはこちら。
    ※例によって、以前作成したSpringのサンプルプロジェクトの流用
     →SpringIDEで始めるSpringMVCプロジェクト

  • HomeController.java
  • @Controller
    public class HomeController {
    
        private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
    
        private static final String CR = "\r";
        private static final String LF = "\n";
        private static final String CRLF = "\r\n";
    
        /**
         * Simply selects the home view to render by returning its name.
         */
        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String home(Locale locale, Model model) {
            logger.info("Welcome home! The client locale is {}.", locale);
    
            Date date = new Date();
            DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
    
            String formattedDate = dateFormat.format(date);
    
            // 改行を含む文字列の作成
            String value =
                    "CR改行" + CR +
                    "LF改行" + LF +
                    "CRLF改行" + CRLF +
                    "<i>最後の行</i>";
    
            model.addAttribute("serverTime", formattedDate );
            model.addAttribute("testNewLineValue", value );
    
            return "home";
        }
    }
    
  • home.jsp
  • <%@ page pageEncoding="UTF-8" %>
    <%@ page session="false" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://opentone.co.jp/sample/tag/NewLineToBR" prefix="br" %>
    <html>
    <head>
        <title>Home</title>
    </head>
    <body>
    <h1>
        Hello world!
    </h1>
    
    <P>  The time on the server is ${serverTime}. </P>
    <h2>【改行を含む文字列表示】</h2>
    <P> ${testNewLineValue} </P>
    
    <h2>【br変換テスト】</h2>
    <P><br:newLineToBr value="${testNewLineValue}"/></P>
    
    </body>
    </html>
    



    1行ですっきり書けました。(名前長いけど…)