1. ホーム
  2. jsp

JSP/Servletの隠し機能 [終了しました]。

2023-08-20 01:55:19

質問

私はJSP/Servletを書くときに使用されるトリックなどに興味があります。まずはこちらから。

私は最近、あるJSPタグの出力を別のタグの属性に含めることができる方法を知りました。

<c:forEach items="${items}">
  <jsp:attribute name="var">
    <mytag:doesSomething/>
  </jsp:attribute>
  <jsp:body>
    <%-- when using jsp:attribute the body must be in this tag --%>
  </jsp:body>
</c:forEach>

どのように解決するのですか?

注:JSP/Servletの隠された機能というのは、なかなか思いつきません。私の意見では、「ベストプラクティス」という表現がより適切で、そのどれかを思いつくことができます。また、JSP/Servletを使った経験があるかどうかにもよります。何年も開発をしていると、これらの"隠された機能"を見なくなるものです。いずれにせよ、私が何年か前に発見した、多くのスターターが十分に認識していない小さな"ベストプラクティス"をいくつかリストアップしてみます。これらは、多くのスターターの目には、「隠れた機能」として分類されるでしょう。とにかく、これがそのリストです :)


JSP ページを直接アクセスから隠す

JSPファイルを /WEB-INF フォルダーに置くことで、例えば次のような直接のアクセスから効果的に隠すことができます。 http://example.com/contextname/WEB-INF/page.jsp . この結果 404 . そうすると、それらにアクセスするには RequestDispatcher でアクセスするか jsp:include .


JSPのためのプリプロセスリクエスト

ほとんどの人はサーブレットの doPost() ポスト -がリクエスト (フォームの送信) を処理することは知っていても、Servlet の doGet() メソッドを使って プリ -JSPへのリクエストを処理します。例えば

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    List<Item> items = itemDAO.list();
    request.setAttribute("items", items);
    request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);
}

これは、JSTL の助けを借りて表示される表形式のデータをあらかじめ読み込むために使われます。 c:forEach :

<table>
    <c:forEach items="${items}" var="item">
        <tr><td>${item.id}</td><td>${item.name}</td></tr>
    </c:forEach>
</table>

このようなサーブレットを url-pattern/page (または /page/* を呼び出すだけです。 http://example.com/contextname/page をブラウザのアドレスバーか単なるリンクで呼び出して実行します。また、例えば サーブレットにおける doGet と doPost .


動的なインクルード

ELは jsp:include :

<jsp:include page="/WEB-INF/${bean.page}.jsp" />

bean.getPage() は単に有効なページャー名を返すことができます。


ELは任意のゲッターにアクセスすることができます

ELは、アクセスされるオブジェクトが必ずしも 完全な Javabeanです。で始まる引数のないメソッドが存在する場合、そのメソッドは get または is は、ELでアクセスするには十分すぎるほどです。例.

${bean['class'].name}

の値を返します。 bean.getClass().getName() ここで getClass() メソッドは実際には Object#getClass() . ただし class はブレース表記で指定します。 [] というのは、ここで述べたように EL式言語におけるinstanceof check .

${pageContext.session.id}

の値を返します。 pageContext.getSession().getId() で有用な、a.o. アプレットはサーブレットのインスタンスと通信できますか? .

${pageContext.request.contextPath}

の値を返します。 pageContext.getRequest().getContextPath() で有用な、a.o. コンテキストのルート名を含まずに相対パスを使用するには?


ELはMapにもアクセスできる

次のようなELの記法

${bean.map.foo}

bean.getMap().get("foo") . もし Map のキーにドットが含まれている場合は、"ブレース記法" を使うことができます。 [] を引用符で囲んでください。

${bean.map['foo.bar']}

に解決されます。 bean.getMap().get("foo.bar") . 動的なキーが必要な場合は、同様にブレース表記を使用しますが、その場合は引用符で囲まないようにします。

${bean.map[otherbean.key]}

に解決されます。 bean.getMap().get(otherbean.getKey()) .


JSTLによるマップの繰り返し処理

この場合 c:forEach を反復するために Map . 各反復は Map.Entry を生成し、それが getKey()getValue() メソッドでアクセスできるようになります(ELでは単に ${entry.key}${entry.value} ). 例

<c:forEach items="${bean.map}" var="entry">
    Key: ${entry.key}, Value: ${entry.value} <br>
</c:forEach>

参照:例 jstlを使ったデバッグ - 具体的にはどうやるの?


JSPで現在の日付を取得する

現在の日付を取得するには jsp:useBean で取得し、JSTL の助けを借りてそれをフォーマットします。 fmt:formatDate

<jsp:useBean id="date" class="java.util.Date" />
...
<p>Copyright &copy; <fmt:formatDate value="${date}" pattern="yyyy" /></p>

これは(現時点では)次のように表示されます: "著作権 © 2010".


簡単なフレンドリーURL

フレンドリーな URL を簡単に作成する方法として HttpServletRequest#getPathInfo() で隠されたJSPを /WEB-INF :

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF" + request.getPathInfo() + ".jsp").forward(request, response);
}

このサーブレットを例えば /pages/* にマップした場合、リクエストは http://example.com/contextname/pages/foo/bar へのリクエストは効果的に /WEB-INF/foo/bar.jsp . さらに一歩進んで、パス情報を / のパス情報を分割し、最初の部分のみを JSP ページの URL とし、残りの部分を "ビジネスアクション" とします (サーブレットを ページコントローラ ). また、例えば デザインパターン Webベースアプリケーション .


ユーザー入力を再表示するには ${param}

暗黙のELオブジェクト ${param} を参照する HttpServletRequest#getParameterMap() は、JSPのフォーム送信後にユーザの入力を再表示するために使用することができます。

<input type="text" name="foo" value="${param.foo}">

これは基本的に request.getParameterMap().get("foo") . また、例えば サーブレットにフォームを送信した後、JSP で HTML フォームフィールドの値を保持するにはどうすればよいですか?

XSS対策もお忘れなく! 次の章を参照してください。


XSSを防止するJSTL

あなたのサイトが XSS を防ぐために必要なことは、(再)表示することです。 ユーザ制御の のデータを JSTL を使って fn:escapeXml または c:out .

<p><input type="text" name="foo" value="${fn:escapeXml(param.foo)}">
<p><c:out value="${bean.userdata}" />


交互に表示 <table> の行と LoopTagStatus

varStatus 属性は、JSTL c:forEach を指定すると LoopTagStatus を返し、その中にいくつかのゲッターメソッド(ELで使用可能!)があります。従って、偶数行であるかどうかを調べるには、単に loop.getIndex() % 2 == 0 :

<table>
    <c:forEach items="${items}" var="item" varStatus="loop">
        <tr class="${loop.index % 2 == 0 ? 'even' : 'odd'}">...</tr>
    <c:forEach>
</table>

で終わることになります。

<table>
    <tr class="even">...</tr>
    <tr class="odd">...</tr>
    <tr class="even">...</tr>
    <tr class="odd">...</tr>
    ...
</table>

CSSで背景色を変えてあげましょう。

tr.even { background: #eee; }
tr.odd { background: #ddd; }


リスト/配列からカンマで区切られた文字列を入力する。 LoopTagStatus :

もうひとつの便利な LoopTagStatus メソッドは isLast() :

<c:forEach items="${items}" var="item" varStatus="loop">
    ${item}${!loop.last ? ', ' : ''}
<c:forEach>

この結果、次のようなものになります。 item1, item2, item3 .


EL機能

EL関数は public static ユーティリティ・メソッドをEL関数として宣言することができます(例えば JSTL関数 のように)、ELで使えるようにします。例

package com.example;

public final class Functions {
     private Functions() {}

     public static boolean matches(String string, String pattern) {
         return string.matches(pattern);
     }
}

/WEB-INF/functions.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 http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
   
    <tlib-version>1.0</tlib-version>
    <short-name>Custom_Functions</short-name>
    <uri>http://example.com/functions</uri>
    
    <function>
        <name>matches</name>
        <function-class>com.example.Functions</function-class>
        <function-signature>boolean matches(java.lang.String, java.lang.String)</function-signature>
    </function>
</taglib>

として使用することができます。

<%@taglib uri="http://example.com/functions" prefix="f" %>

<c:if test="${f:matches(bean.value, '^foo.*')}">
    ...
</c:if>


元のリクエストURLとクエリ文字列を取得する

JSPが転送されてきた場合、以下の方法で元のリクエストURLを取得することができます。

${requestScope['javax.servlet.forward.request_uri']} 

で元のリクエストクエリー文字列を

${requestScope['javax.servlet.forward.query_string']}


今のところこれだけです。いずれまた追加するかもしれません。