Lidando com HTTP nos Servlets

Programação Java para Web

Prof. Wagner Macedo

## HTTP nos Servlets - [Escrita da resposta](#sec-escrita-resposta) - [Leitura da requisição](#sec-leitura-requisicao) - [Redirecionamento](#sec-redirecionamento) - [Cookies](#sec-cookies) - [Cache do lado cliente](#sec-cache-lado-cliente)

Respostas HTTP


      HTTP/1.1 200 OK
      Content-Type: text/html
      Content-Language: pt-BR
      Content-Length: 37
      Set-Cookie: Variant=1; Theme=blue;
      Expires: Fri, 25 Aug 2017 13:45:42 GMT

      <html><body>Hello World</body></html>
    

      HTTP/1.1 400 Bad Request
      Content-Type: text/html
      Content-Length: 41

      <html><body>400 Bad Request</body></html>
    

Escrita da Resposta


      // Código de Estado
      response.setStatus(200);

      // Cabeçalhos
      response.setHeader("Content-Type", "text/html");
      response.setHeader("Content-Language", "pt-BR");
      response.setHeader("Content-Length", "37");
      response.setHeader("Set-Cookie", "Variant=1; Theme=blue;");
      response.setHeader("Expires", "Fri, 25 Aug 2017 13:45:42 GMT");

      // Corpo
      PrintWriter out = response.getWriter();
      out.println("<html><body>Hello World</body></html>");
    

Valores tipados nos cabeçalhos

Abstrações do servlet para tipos inteiros e datas


        int interval = getIntervaloPadrao() + 60;
        response.setIntHeader("Retry-After", interval);
      

        // Java 8
        long expires = Instant.now().plusSeconds(60).toEpochMilli();
        response.setDateHeader("Expires", expires);
      

      // Antes
      response.setHeader("Content-Length", "37");

      // Depois
      response.setIntHeader("Content-Length", 37);
    

      // Antes
      response.setHeader("Expires", "Fri, 25 Aug 2017 13:45:42 GMT");

      // Depois
      long expires = Date.UTC(2017, 8, 25, 13, 45, 42);
      response.setDateHeader("Expires", expires);
    

O código abaixo pode não funcionar!


      PrintWriter conteudo = response.getWriter();
      conteudo.println("<html><body>Exemplo</body></html>");

      response.setStatus(200);
      response.setHeader("Content-Type", "text/html");
      response.setHeader("Content-Language", "pt-BR");
    

O código de estado e os cabeçalhos são enviados para o cliente antes do corpo da resposta.

Métodos de Atalho para Cabeçalhos

Antes


          response.setHeader("Content-Type", "text/html");
        

Depois


          response.setContentType("text/html");
        

        response.setHeader("Content-Type", "text/html; charset=utf-8");
      

        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
      

        response.setIntHeader("Content-Length", 37);
      

        response.setContentLength(37);
      

        response.setHeader("Set-Cookie", "Variant=1; Theme=blue;");
      

        response.addCookie(new Cookie("Variant", "1"));
        response.addCookie(new Cookie("Theme", "blue"));
      

Páginas de Erro

O método sendError emite uma página de erro.


      response.sendError(400);
    
Páginas de Erro Configuráveis

Você pode configurar páginas de erro no web.xml


      <error-page>
        <error-code>404</error-code>
        <location>/erros/404-not-found.jsp</location>
      </error-page>
    

Requisições HTTP


      GET /blog?post=2707 HTTP/1.1
      Host: www.exemplo.com.br
      User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36
      Accept: text/html
      Accept-Charset: UTF-8
      Accept-Language: pt-br
      Cookie: Variant=1; Theme=blue;
      Referer: http://exemplo.com/index.php
      If-Modified-Since: Sat, 07 Aug 2017 21:18:31 GMT
       
    

      POST /blog/cadastro.php HTTP/1.1
      Host: www.exemplo.com.br
      Content-Type: application/x-www-form-urlencoded
      Content-Length: 35
      Cookie: Variant=1; Theme=blue;
      Referer: http://exemplo.com/blog?post=2707

      name=John+Darling&address=Neverland
    

Leitura da Requisição


      // Método da requisição
      String metodo = request.getMethod();

      // URI da requisição
      String uri = request.getRequestURI();

      // Query string, se houver
      String queryString = request.getQueryString();

      // Cabeçalhos
      String host       = request.getHeader("Host");
      String userAgent  = request.getHeader("User-Agent");
      String accept     = request.getHeader("Accept");
      String acceptLang = request.getHeader("Accept-Language");
      String cookie     = request.getHeader("Cookie");

      // Corpo da requisição
      BufferedReader reader = request.getReader();
    

Cabeçalhos de Requisição Tipados


      // Antes
      String headerCL = request.getHeader("Content-Length");
      int contentLength = Integer.parseInt(headerCL);

      // Depois
      int contentLength = request.getIntHeader("Content-Length");
    

      // Antes
      String headerIMF = request.getHeader("If-Modified-Since");
      long ifModified = Long.parseLong(headerIMF);

      // Depois
      long ifModified = request.getDateHeader("If-Modified-Since");
    

Métodos de Atalho para Cabeçalhos

Antes


          String ct = request.getHeader("Content-Type");
        

Depois


          String ct = request.getContentType();
        

        String charset = null;
        String[] contentType = request.getContentType().split(";");
        for (String ct : contentType) {
            int index = ct.indexOf("charset=");
            if (index != -1) {
                charset = ct.substring(index + 8).trim();
                break;
            }
        }
      

        String charset = request.getCharacterEncoding();
      

        int cl = request.getIntHeader("Content-Length");
      

        int cl = request.getContentLength();
      

Redirecionamento HTTP

Principais Códigos de Redirecionamento

301 Moved Permanently (Movido Permanentemente)
Esta requisição e as próximas devem ir para uma dada URI.
302 Found (Encontrado)
O site foi movido temporariamente para uma dada URI.
303 See Other (Veja Outro)
Semelhante ao código 302.
304 Not Modified (Não Modificado)
Redirecionamento para o cache local do browser.
307 Temporary Redirect (Redirecionamento Temporário)
Semelhante ao código 302.

Se o lado servidor da Wikipédia for em Java, provavelmente terá o seguinte código.


      response.setStatus(301);
      response.setHeader("Location", "https://www.wikipedia.org/");
    

Os trechos de código abaixo são equivalentes


      response.setStatus(302);
      response.setHeader("Location", "https://www.wikipedia.org/");
    

      response.sendRedirect("https://www.wikipedia.org/");
    

Cookies HTTP

cookies

O que são cookies?

Cookie é uma string armazenada no lado cliente.

Os browsers enviam para o servidor os cookies armazenados em todas as requisições.

Segurança

Os cookies podem ter informações muito sensíveis 🔥, como o identificador de sessão do usuário.

Em posse desse identificador, é possível, por exemplo, acessar a conta de um usuário sem precisar da senha.

Por causa disso, os browsers só enviam cookies para os servidores que são donos daqueles cookies.

Como cookies funcionam?

  1. O browser faz uma requisição para http://www.exemplo.com
  2. Na resposta do servidor, aparece o cabeçalho Set-Cookie
    
              HTTP/1.1 200 OK
              …
              Set-Cookie: Variant=1; Theme=blue;
              …
            
  3. O browser armazena na lista de cookies do site www.exemplo.com o cookie Variant com o valor "1" e o cookie Theme com o valor "blue"
  4. Nas próximas requisições feitas à http://www.exemplo.com, o browser envia os dois cookies armazenados no cabeçalho Cookie
    
              GET / HTTP/1.1
              Host: www.exemplo.com.br
              …
              Cookie: Variant=1; Theme=blue;
              …
        

Enviando Cookies para o browser


      response.addCookie(new Cookie("Variant", "1"));
      response.addCookie(new Cookie("Theme", "blue"));
    

Obtendo os Cookies da requisição


      // Obtém o tema e o valor da variante do usuário
      String theme = null;
      int variant = -1;

      for (Cookie ck : request.getCookies()) {
          if (ck.getName().equals("theme")) {
              theme = ck.getValue();
          }
          if (ck.getName().equals("variant")) {
              variant = Integer.parseInt(ck.getValue());
          }
      }
    

Cache HTTP

O protocolo HTTP especifica que o conteúdo da resposta do método GET pode ser mantido em cache.

Processo de Cache do Lado Cliente

  1. Cliente faz a primeira requisição:

        GET /index.html HTTP/1.1
        Host: www.exemplo.com.br
        …
      
  1. Servidor dá uma resposta normal:

        HTTP/1.1 200 OK
        Last-Modified: Fri, 25 Aug 2017 21:15:59 GMT
        …
      
  1. Nas próximas requisições, cliente se refere à data da última modificação:
    
                GET /index.html HTTP/1.1
                Host: www.exemplo.com.br
                If-Modified-Since: Fri, 25 Aug 2017 21:15:59 GMT
                …
              
  2. Caso o recurso não tenha sido modificado, retorna código 304.
    
                HTTP/1.1 304 Not Modified
                …
              

Se houve modificações, teremos uma nova resposta de código 200, a data de modificação atualizada, o ciclo reinicia!

Lidando com cache no Servlet


        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html; charset=utf-8");

            PrintWriter conteudo = response.getWriter();
            conteudo.println("Última modificação: " + lastModified);
        }

        // O servlet lida com cache HTTP enviando o cabeçalho Last-Modified
        private Instant lastModified = null;

        @Override
        protected long getLastModified(HttpServletRequest request) {
            if (lastModified == null) {
                lastModified = Instant.now();
            } else {
                Instant now = Instant.now();
                long nowSeconds = now.getEpochSecond();
                long lastSeconds = lastModified.getEpochSecond();

                if (nowSeconds >= lastSeconds + 30) {
                    lastModified = now;
                }
            }

            return lastModified.toEpochMilli();
        }
      

Servlet de Aprendizado

Baixe o código de RequisicaoServlet e faça requisições pelo Navegador Web e/ou Postman para aprender mais sobre os métodos do servlet usados para ler a requisição do cliente.

Leitura Recomendada 🔖

Capítulo 6, 7, 8 e 11

Dúvidas?

Este slide pode ser encontrado em:
http://prof.wagnermacedo.com/2017-2/PJW/6