PA165/Cvičení webové aplikace 1 - Servlet API 3.0: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
(Servlet)
(Komentáře)
Řádka 130: Řádka 130:
 
* při zasílání binárního obsahu můžeme pomocí HTTP hlavičky ''Content-disposition'' určit, zda se má přímo zobrazit (hodnota ''inline'') nebo uložit (hodnota ''attachment'')
 
* při zasílání binárního obsahu můžeme pomocí HTTP hlavičky ''Content-disposition'' určit, zda se má přímo zobrazit (hodnota ''inline'') nebo uložit (hodnota ''attachment'')
 
* při přesměrování (redirect) je nutné před URL přidat contextPath, URL prohnat metodou encodeRedirectURL() a hodnoty případných parametrů musí být URL-encoded
 
* při přesměrování (redirect) je nutné před URL přidat contextPath, URL prohnat metodou encodeRedirectURL() a hodnoty případných parametrů musí být URL-encoded
 +
* u URL chráněného heslem jdou jméno a heslo nezašifrovaně, jen spojené dvojtečkou a zapsané pomocí Base64
 
* místo obsahu a přesměrování můžeme poslat prohlížeči chybové hlášení metodou sendError()
 
* místo obsahu a přesměrování můžeme poslat prohlížeči chybové hlášení metodou sendError()
  

Verze z 5. 11. 2012, 13:48


Prostředí

  • NetBeans 7.2
  • Oracle JDK 7
  • Tomcat 7
  • Maven 3
module add netbeans-7.2 jdk-1.7.0_03 maven-3.0.4

Vytvoření projektu

Vytvořte v NetBeans nový projekt pro webovou aplikaci typu Maven tj. File - New project - Categories:Maven - Project: Web Application, položku Package zvolte jako cz.muni.fi.pa165.web1.

Vytvořený projekt zkuste spustit (klávesa F6), měl by se otveřít prohlížeč a v něm stránka s nápisem Hello World!.


úvodní stránka

Zeditujte soubor index.jsp a vyměňte jeho obsah za následující HTML5:

 
<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <title>Domov</title>
    </head>
    <body>
        <h1>Sláva, funguje to!</h1>
        <ul>
            <li><a href="${pageContext.request.contextPath}/text/primo?a=1&b=ccc">text přímo ze servletu</a></li>
            <li><a href="${pageContext.request.contextPath}/text/zjsp">text z JSP</a></li>
            <li><a href="${pageContext.request.contextPath}/soubor">soubor</a></li>
            <li><a href="${pageContext.request.contextPath}/presmerovani">přesměrování</a></li>
            <li><a href="${pageContext.request.contextPath}/chranene">chráněné heslem</a></li>
            <li><a href="${pageContext.request.contextPath}/text/ble">chyba</a></li>
        </ul>
    </body>
</html>

Výraz ${pageContext.request.contextPath} vkládá před relativní odkazy prefix servletové aplikace.

Servlet

V package cz.muni.fi.pa165.web1 vytvořte novou třídu servletu kliknutím pravým tlačítkem na balík, vyberte New - Java class a pojmenujte ji MyDemoServlet.

Přepište zdrojový kód třídy na:

 
package cz.muni.fi.pa165.web1;
 
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.*;
import java.util.zip.*;
 
@WebServlet(urlPatterns = {"/text/*", "/soubor/*", "/chranene/*", "/presmerovani/*"})
public class MyDemoServlet extends HttpServlet {
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if ("/text".equals(request.getServletPath()) && "/primo".equals(request.getPathInfo())) {
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.println("<pre>text přímo ze servletu");
            out.println("serverInfo=" + getServletContext().getServerInfo());
            out.println("parametry:");
            for (String p : Collections.list(request.getParameterNames())) {
                out.println(p + "=" + request.getParameter(p));
            }
            return;
        } else if ("/text".equals(request.getServletPath()) && "/zjsp".equals(request.getPathInfo())) {
            request.setAttribute("seznam", Arrays.asList("mléko", "rohlíky", "salám"));
            request.getRequestDispatcher("/stranka2.jsp").forward(request, response);
            return;
        } else if ("/soubor".equals(request.getServletPath())) {
            response.setContentType("application/zip");
            response.setHeader("Content-disposition", "attachment; filename=\"neco.zip\"");
            ServletOutputStream sos = response.getOutputStream();
            ZipOutputStream zos = new ZipOutputStream(sos);
            zos.putNextEntry(new ZipEntry("neco.txt"));
            zos.write("nějaký text".getBytes());
            zos.putNextEntry(new ZipEntry("necojineho.txt"));
            zos.write("jiný text".getBytes());
            zos.close();
            return;
        } else if ("/presmerovani".equals(request.getServletPath())) {
            String url = request.getContextPath() + "/text/primo?z=" + URLEncoder.encode("?/ =", "utf-8");
            response.sendRedirect(response.encodeRedirectURL(url));
        } else if ("/chranene".equals(request.getServletPath())) {
            String auth = request.getHeader("Authorization");
            if (auth == null) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setHeader("WWW-Authenticate", "Basic realm=\"zadej heslo\"");
                return;
            }
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
            String[] creds = new String(DatatypeConverter.parseBase64Binary(auth.split(" ")[1])).split(":", 2);
            out.println("<pre>zadáno jméno a heslo " + creds[0] + ":" + creds[1]);
            return;
        } else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Nerozpoznane URL");
            return;
        }
    }
}

Opět spusťte aplikaci a proklikejte si odkazy.

Komentáře

Povšimněte si ve výše uvedeném kódu servletu následujících věcí:

  • třída servletu je mapována na URL pomocí anotace @WebServlet
  • mapované URL se skládá ze tří částí - contextPath, servletPath a pathInfo
  • metoda request.getParameterNames() vrací z historických důvodů java.util.Enumeration, na Iterable ji zkonvertujeme pomocí Collections.list()
  • všechny parametry requestu jsou typu String
  • při přeposlání (forward) na jiný servlet/JSP se data předávají pomocí metody setAttribute()
  • při zasílání binárního obsahu můžeme pomocí HTTP hlavičky Content-disposition určit, zda se má přímo zobrazit (hodnota inline) nebo uložit (hodnota attachment)
  • při přesměrování (redirect) je nutné před URL přidat contextPath, URL prohnat metodou encodeRedirectURL() a hodnoty případných parametrů musí být URL-encoded
  • u URL chráněného heslem jdou jméno a heslo nezašifrovaně, jen spojené dvojtečkou a zapsané pomocí Base64
  • místo obsahu a přesměrování můžeme poslat prohlížeči chybové hlášení metodou sendError()

JSP - Java Server Page

V předchozí ukázce nefungoval odkaz /text/zjsp, protože zatím neexistovala JSP stránka, na kterou se předávalo řízení.

Nyní vytvořte v adresáři src/main/webapp soubor stranka.jsp a zeditujte mu obsah na následující:

 
<%@page import="java.util.List"%>
<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
  <title>JSP stránka</title>
  <style>
   #nadpis { color: red; }   
   table.zakladni { border-collapse: collapse; margin: 10px; }
   table.zakladni th, table.zakladni td {  border: solid 1px black; padding: 4px; }
  </style>
  <script>
      console.log("ahoj");
  </script>
</head>
<body>
  <h1 id="nadpis">JSP stránka</h1>
  <p>seznam</p>
  <table class="zakladni">
      <% List<String> seznam = (List<String>) pageContext.findAttribute("seznam");
      for(int i=0,n=seznam.size();i<n;i++) { %>          
      <tr>
          <td><%=i%></td>
          <td><%=seznam.get(i)%></td>
      </tr>
      <% } %>
  </table>
</body>
</html>

Spusťte znovu aplikaci z vyzkoušejte stránku. Ve Firefoxu po zmáčknutí Ctrl+Shift+K nebo v Chrome po zmáčknutí F12 můžete vidět JavaScriptovou konzolu z výpisem skriptu.

Komentáře

  • na začátku JSP stránky je důležité definovat kódování výstupu parametrem charset, jinak se použije iso-8859-1
  • pomocí CSS selektoru #nadpis nastavujeme vzhled právě jednoho prvku s atributem id="nadpis"
  • pomocí CSS selektoru .zakladni nastavujeme vzhled všech prvků s atributem class="zakladni"
  • v JavaScriptu můžeme psát trasovací výpisy pomocí console.log()
  • příkazy v jazyce Java se vkládají mezi <% %>
  • v JSP vždy existuje implicitní proměnná pageContext typu PageContext
  • v JSP vždy existuje implicitní proměnná out typu JspWriter
  • místo <%out.print(i);%> lze zkráceně psát <%=i%>
  • přímé vypisování textů je nebezpečné, mohou obsahovat speciální znaky z HTML <>& ale není standardní třída pro HTML-encoding pomocí entit &lt; &gt; &amp;

JSP stránka s JSTL

Místo kodu v Javě lze výhodně využít tzv. JSTL (JSP Standard Tag Library). Ta však není standardně součástí servletového containeru.

Aktivace JSTL

Do souboru pom.xml přidejte tuto závislost před tag </dependencies>:

 
   <dependency>
     <groupId>org.glassfish.web</groupId>
     <artifactId>javax.servlet.jsp.jstl</artifactId>
     <version>1.2.2</version>
   </dependency>

Použití

V servletu změňte forward ze stranka.jsp na stranka2.jsp a vytvořte soubor src/main/webapp/stranka2.jsp s následujícím obsahem:

 
<%@page contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP stránka</title>
    <style>
        #nadpis { color: green; }
        table.zakladni { border-collapse: collapse; margin: 10px; }
        table.zakladni th, table.zakladni td {  border: solid 1px black; padding: 4px; }
    </style>
    <script>
        console.log("ahoj");
    </script>
</head>
<body>
<h1 id="nadpis">JSP stránka</h1>
<p>seznam</p>
<table class="zakladni">
    <c:forEach items="${seznam}" var="s" varStatus="i">
    <tr>
        <td>${i.count}</td>
        <td><c:out value="${s}"/></td>
    </tr>
    </c:forEach>
</table>
</body>
</html>

Komentáře

  • na začátku JSP je třeba knihovny značek navázat na nějaké prefixy pomocí direktiv <%@ taglib %>
  • značka <c:forEach> iteruje svoje tělo přes položky zadané atributem items, v něm lze výhodně použít jazyk EL
  • značka <c:forEach> ukládá aktuální položku do atributu pageContext určeného atributem značky var
  • pomocí atributu značky varStatus lze získat objekt typu LoopTagStatus, pomocí které lze získat pozici v rámci iterace
  • značka <c:out> zajistí HTML-encoding speciálních znaků