Versteckte Funktionen von JSP / Servlet [geschlossen]

77

Ich interessiere mich für Ihre Tricks usw., die beim Schreiben von JSP / Servlet verwendet werden. Ich werde beginnen:

Ich habe kürzlich herausgefunden, wie Sie die Ausgabe eines JSP-Tags in ein Attribut eines anderen Tags aufnehmen können:

<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>
mkoryak
quelle

Antworten:

155

Hinweis: Es fällt mir schwer, mir "versteckte Funktionen" für JSP / Servlet vorzustellen. Meiner Meinung nach ist "Best Practices" eine bessere Formulierung, und ich kann mir jede davon vorstellen. Es hängt auch wirklich von Ihrer Erfahrung mit JSP / Servlet ab. Nach Jahren der Entwicklung sehen Sie diese "versteckten Funktionen" nicht mehr. Auf jeden Fall werde ich einige dieser kleinen "Best Practices" auflisten, von denen ich jahrelang herausgefunden habe, dass viele Starter sich dessen nicht voll bewusst sind. Diese würden im Auge vieler Starter als "versteckte Merkmale" eingestuft. Wie auch immer, hier ist die Liste :)


Verstecken Sie JSP-Seiten vor dem direkten Zugriff

Indem Sie JSP-Dateien in einem /WEB-INFOrdner ablegen, verbergen Sie sie beispielsweise effektiv vor dem direkten Zugriff http://example.com/contextname/WEB-INF/page.jsp. Dies führt zu a 404. Sie können dann nur über ein RequestDispatcherIn-Servlet oder mit darauf zugreifen jsp:include.


Vorverarbeitungsanforderung für JSP

Die meisten sind über Servlet bewusst schreiben eine Anfrage (ein Formular) -Prozess, aber die meisten wissen nicht , dass Sie Servlet verwenden Methode vorab eine Anfrage für eine JSP -Prozess. Zum Beispiel:doPost()doGet()

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);
}

Hiermit werden einige tabellarische Daten vorgeladen, die mithilfe von JSTLs angezeigt werden sollen c:forEach:

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

Ordnen Sie ein solches Servlet einem url-patternvon /page(oder /page/*) zu und rufen Sie es einfach über die http://example.com/contextname/pageAdressleiste des Browsers oder einen einfachen Vanille-Link auf, um es auszuführen. Siehe auch zB doGet und doPost in Servlets .


Dynamische Includes

Sie können EL verwenden in jsp:include:

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

Sie bean.getPage()können nur einen gültigen Seitennamen zurückgeben.


EL kann auf jeden Getter zugreifen

EL verlangt per se nicht, dass das Objekt, auf das zugegriffen werden soll, ein vollwertiger Javabean ist. Das Vorhandensein einer No-Arg-Methode, der ein Präfix vorangestellt ist getoder ismehr als ausreichend ist, um in EL darauf zuzugreifen. Z.B:

${bean['class'].name}

Dies gibt den Wert zurück, von bean.getClass().getName()dem die getClass()Methode tatsächlich geerbt wird Object#getClass(). Beachten Sie, dass dies aus den hier genannten Gründen unter classVerwendung der "Klammer-Notation" angegeben wird .[]

${pageContext.session.id}

Dieser gibt den Wert von pageContext.getSession().getId()der in ao nützlich ist , kann ein Applet kommuniziert mit einer Instanz eines Servlets .

${pageContext.request.contextPath}

Dies gibt den Wert zurück, der pageContext.getRequest().getContextPath()in ao nützlich ist. Wie verwende ich relative Pfade ohne den Kontextstammnamen?


EL kann auch auf Karten zugreifen

Die folgende EL-Notation

${bean.map.foo}

beschließt zu bean.getMap().get("foo"). Wenn der MapSchlüssel einen Punkt enthält, können Sie die "Klammernotation" []mit einem zitierten Schlüssel verwenden:

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

was sich auflöst bean.getMap().get("foo.bar"). Wenn Sie einen dynamischen Schlüssel möchten, verwenden Sie auch die Klammernotation, die jedoch nicht in Anführungszeichen gesetzt ist:

${bean.map[otherbean.key]}

was sich auflöst bean.getMap().get(otherbean.getKey()).


Iterieren Sie mit JSTL über die Karte

Sie können auch verwenden c:forEach, um über a zu iterieren Map. Jede Iteration gibt ein Map.Entrywas wiederum hat getKey()und getValue()Methoden (so dass Sie einfach in EL durch ${entry.key}und darauf zugreifen können ${entry.value}). Beispiel:

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

Siehe auch zB Debuggen mit jstl - wie genau?


Aktuelles Datum in JSP abrufen

Sie können das aktuelle Datum mit jsp:useBeanabrufen und mithilfe von JSTL formatierenfmt:formatDate

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

Dies wird (ab sofort) wie folgt gedruckt: "Copyright © 2010".


Einfache freundliche URLs

Eine einfache Möglichkeit, benutzerfreundliche URLs zu haben, besteht darin, JSPs zu verwenden HttpServletRequest#getPathInfo()und zu verbergen in /WEB-INF:

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

Wenn Sie dieses Servlet beispielsweise zuordnen /pages/*, wird eine Anforderung am http://example.com/contextname/pages/foo/bareffektiv angezeigt /WEB-INF/foo/bar.jsp. Sie können einen Schritt weiter gehen, indem Sie die Pfadinfo aufteilen /und nur den ersten Teil als JSP-Seiten-URL und den Rest als "Geschäftsaktionen" verwenden (lassen Sie das Servlet als Seiten-Controller fungieren ). Siehe auch zB webbasierte Anwendungen für Entwurfsmuster .


Benutzereingaben mit erneut anzeigen ${param}

Das implizite EL-Objekt, ${param}das sich auf das bezieht, HttpServletRequest#getParameterMap()kann verwendet werden, um Benutzereingaben nach dem Senden eines Formulars in JSP erneut anzuzeigen:

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

Dies macht im Grunde das gleiche wie request.getParameterMap().get("foo"). Siehe auch zB Wie kann ich HTML-Formularfeldwerte in JSP beibehalten, nachdem ich das Formular an Servlet gesendet habe?
Vergessen Sie nicht, XSS zu verhindern! Siehe folgendes Kapitel.


JSTL, um XSS zu verhindern

Um zu verhindern, dass Ihre Site XSS enthält , müssen Sie lediglich benutzergesteuerte Daten mit JSTL fn:escapeXmloder (erneut) anzeigen c:out.

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

Abwechselnde <table>Zeilen mitLoopTagStatus

Das varStatusAttribut von JSTL c:forEachgibt Ihnen ein LoopTagStatusBack, das wiederum mehrere Getter-Methoden hat (die in EL verwendet werden können!). Um nach geraden Zeilen zu suchen, prüfen Sie einfach, ob 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>

was effektiv in enden wird

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

Verwenden Sie CSS, um ihnen eine andere Hintergrundfarbe zu geben.

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

Füllen Sie die kommasparierte Zeichenfolge aus List / Array mit LoopTagStatus:

Eine weitere nützliche LoopTagStatusMethode ist die isLast():

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

Was zu so etwas führt item1, item2, item3.


EL-Funktionen

Sie können public staticDienstprogrammmethoden als EL-Funktionen deklarieren (z. B. als JSTL-Funktionen ), damit Sie sie in EL verwenden können. Z.B

package com.example;

public final class Functions {
     private Functions() {}

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

mit /WEB-INF/functions.tlddem wie folgt aussehen:

<?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>

welches als verwendet werden kann

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

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

Rufen Sie die ursprüngliche Anforderungs-URL und die Abfragezeichenfolge ab

Wenn die JSP weitergeleitet wurde, können Sie die ursprüngliche Anforderungs-URL erhalten über:

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

und die ursprüngliche Anforderungsabfragezeichenfolge von,

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

Das war es soweit. Vielleicht füge ich früher oder später noch etwas hinzu.

BalusC
quelle
20
Dies ist einer der umfangreichsten Einträge hier auf Stackoverflow. Keine Tricks, wie Sie sagten, aber gute Kenntnisse und gängige Praktiken, die jeder, der sein Salz wert ist, kennen sollte. Für die alternierenden Tabellenzeilen ist es besser, die moderne CSS-Syntax zu verwenden und mit tr: nth-child (gerade) zu kolorieren. Dadurch wird Ihre HTML-Ausgabe noch sauberer.
Photodeus
6
Immer perfekt Mr BalusC :)
Muhammad Hewedy
1
Wow, soooo informative Antwort und großartige gängige Praktiken, vielen Dank an BlausC.
PalAlaa
Ich habe gehört, dass die Verwendung von Date zum Abrufen des Jahres veraltet ist. Kann ich den Kalender zum Formatieren verwenden?
Itsraja
1
+1 Mein zweiter Favorit von BalusC nach seinem in JDK verwendeten Design Pattern.
Zawhtut