Prévia do material em texto
5ºAula HTTP Server Objetivos de aprendizagem Ao término desta aula, vocês serão capazes de: • construir um web server; • retornar arquivos externos pelo servidor; • manipular os dados de um formulário; • fazer upload de arquivos para o servidor; • enviar e-mail pelo servidor. Olá, Bem-vindos(as) à aula 5 da disciplina de Desenvolvimento Web II. Na aula anterior, o gerenciador de pacotes do Node.js foi apresentado, além disso indicamos alguns módulos de terceiros com funcionalidades importantes, tais como manipulação de string, cores e data, criação de pequeno banco de dados JSON, além de um módulo de execução automática. Nesta aula, será demonstrado todo o potencial do JavaScript no lado do servidor, criando um web server. Leiam atentamente esta aula e se tiverem alguma dúvida, usem os recursos que estão na sua área do aluno. Boa aula! Bons estudos! Desenvolvimento Voltado a Web III 38 1 – Módulo HTTP 2 – Módulo URL 3 – Módulo Sistema de Arquivos 4 – Módulo Formulário 5 – Módulo E-mail 1 - Módulo HTTP Na aula anterior, instalamos diversos módulos de terceiros, com as mais diversas funcionalidades. O Node.js nativamente já possui alguns módulos que permitem trabalhar com diversos protocolos, tais como: HTTP, HTTPS, FTP, SSH, DNS, UDP e WebSockets. Nesta seção usaremos o módulo nativo HTTP para desenvolver um sistema web, tratando do roteamento e carregamento da página. Pereira (2014) explica que as aplicações web necessitam que o servidor disponibilize seus recursos e, para esse caso, define que programando-o no Node.js “desenvolve uma aplicação middleware, ou seja, além de programar as funcionalidades da sua aplicação, você também programa códigos de configuração de sua infraestrutura”. O módulo nativo HTTP do Node.js é suficiente para o desenvolvimento de pequenos servidores HTTP, conforme o tamanho da aplicação é indicada a utilização de um framework que implementa funções e configurações que facilitem o desenvolvimento, tal como o framework Express. O interessante da utilização de frameworks é que eles facilitam a construção, já que disponibilizam uma estrutura mínima funcional para o que se precisa. Antes de utilizar um framework é importante entender todo o conceito do HTTP, portanto, utilizaremos o modulo nativo do Node.js. HTTP HTTP é o protocolo de aplicação mais importante sobre o TCP/IP, que é o conjunto de protocolos que fazem a internet funcionar. Esse protocolo permite que browsers (cliente-side) se comuniquem com servidores remotos (server-side) (COPES, 2019). Para que essa comunicação seja possível, o HTTP transfere arquivos HiperTexto pela internet. Esse arquivo será retornado quando o lado do cliente solicitar ao protocolo através de um link, que pode ser um endereço ip ou um nome de domínio (http:/unigran.br). O protocolo trabalha com request e response: o lado do cliente faz uma solicitação (request) e o servidor responde (response). O request pode ser feito por GET, POST, HEAD, PUT, DELETE, OPTIONS e TRACE. Assim como todos os módulos já vistos, o HTTP precisa ser carregado para poder ser utilizado. Criamos, então, uma constante http que vai carregar esse módulo. const http = require(‘http’); Com a constante definida utilizamos, então, um método Seções de estudo para a criação do servidor e outro para definir em qual porta o servidor estará escutando as solicitações, createServer() e listen(), respectivamente. O primeiro recebe por parâmetro uma função que possui request e response também como parâmetro, enquanto o segundo recebe o número da porta pela qual o servidor deverá aguardar as solicitações. const server = http. createServer((request,response)=>{ c o n s o l e . l o g ( ‘ S o l i c i t a ç ã o realizada!’); }); server.listen(3000); Podemos, então, colocar o nosso servidor para rodar no prompt; fazemos uma chamada a node, passando o nome do arquivo do servidor. Para realizar essa chamada pode-se utilizar o navegador, através da url http://localhost:3000. Nesse caso, estamos solicitando a um servidor local, na porta 3000, que responda nossa requisição. Figura 1 – Servidor rodando. Fonte: Acervo pessoal. Ao colocar o servidor para rodar não irá aparecer nada no console do prompt, já que o nosso código só apresenta na tela a string ‘Solicitação realizada!’, quando o navegador faz efetivamente uma request. Cada vez que fizermos uma solicitação no navegado aparecerá no console a string que passamos. Como nosso servidor ainda não envia uma resposta, o navegador vai retornar o erro “ERR_EMPTY_ RESPONSE” já que nosso servidor não enviou nenhum dado de volta. Para resolver esse problema vamos enviar uma resposta para o nosso navegador, através do método response.write(), que irá enviar para o navegador uma string que for passada por parâmetro a ele. Devemos ainda enviar um cabeçalho que irá informar o código de status da resposta response.writeHead() e finalizar o envio com response.end(). Se não colocar o end, o navegador não vai entender que é o fim da resposta e não irá parar de carregar. const http = require(‘http’); const server = http. createServer((request,response)=>{ res.writeHead(200, {‘Content- Type’:‘text-html’}); res.write(‘<h1>Hello World!</ h1>’); res.end(); 39 }); server.listen(3000); Figura 2 – Hello World. Fonte: Acervo pessoal. Como vimos, na criação do servidor, dois parâmetros são passados para nossa função, o request e o response, enquanto o segundo é usado para ir construindo a nossa página, como já foi visto, o primeiro retorna algumas informações relevantes para entender de onde está vindo a requisição e o quais suas propriedades. Através do console.log(request) podemos observar o conteúdo desse objeto. Duas propriedades importantes que podemos acessar diretamente é o caminho do endereço acessado (request.url) e o método da solicitação (request. method). Para facilitar a visualização é possível utilizar o módulo colors estudado na aula 04. const http = require(‘http’); const colors = require(‘colors’); const server = http. createServer((request,response)=>{ console.log(‘request.method=’. yellow,request.method); console.log(‘request.url=’. yellow,request.url); }); server.listen(3000); No código acima, quando o servidor receber uma requisição ele irá apresentar no console o método que o request foi feito e também qual a url que foi chamada. Se a url digitada no navegador for http://localhost:3000/, o resultado pode ser visto na Figura 3, e se for http://localhost:3000/ index.html, o resultado pode ser visto na Figura 4. Figura 3 – Propriedades request. Fonte: Acervo pessoal. Figura 4 – Propriedades request. Fonte: Acervo pessoal. Outra propriedade importante que pode ser obtida através do request é o headers, que pode ser acessado diretamente, como feito com o method e url. Ele retorna informações sobre a conexão como user-agent e cookie. Figura 5 – Propriedades headers. Fonte: Acervo pessoal. Um servidor pode também se comportar com uma API, que retorna uma informação no formato JSON. A escolha pelo envio desse método pode ser feita se na url que fez a chamada tiver a palavra json “http://localhost:3000/json”. const http = require(‘http’); const server = http. createServer((request,response)=>{ if(request.url == “/json”) { r e s p o n s e . setHeader(‘Content-type’,’application/json’); var carro = { “modelo”: “Argo”, “marca”: “Fiat”, “ano”: 2019, “revisoes”: [“2018-05-10”,”2018-08-25”] }; r e s p o n s e . e n d ( J S O N . stringify(carro)); } }); server.listen(3000); Desenvolvimento Voltado a Web III 40 Figura 5 – Resultado JSON. Fonte: Acervo pessoal. Para visualizar melhor a estrutura de resposta no formato JSON é interessante instalar uma extensão no seu navegador, que te crie a estrutura em árvore, que representa o JSON. Para o Chrome, usado no desenvolvimento deste guia de estudos, a extensão pode ser encontrada na loja de extensõesdo Chrome com o nome de JSONView. Você pode escolher a extensão que melhor se encaixe para o que você precisa. 2 - Módulo URL A construção da URL é importante, pois ela irá informar ao servidor o que o lado cliente está querendo ver. No exemplo anterior, a URL fazia uma chamada simplesmente acrescentando a palavra json no final da URL. E se for preciso passar mais informações ao mesmo tempo para o servidor? Para isso utilizamos o módulo url, que consegue quebrar o endereço web em várias partes. Por exemplo, temos a url “http://localhost:3000/index.html?ano=2019&mes=maio”, que está no formato mais comum encontrado na internet, passando informações por GET, o módulo em questão irá então separar toda a URL em host, pathname e search. var url = require(‘url’); var colors = require(‘colors’); var webadr = ‘http:// localhost:3000’+ ‘ / index.html?’+ ‘ano=2019&mes=maio’; var q = url.parse(webadr, true); console.log(‘host = ‘.red,q. host); console.log(‘pathname = ‘.red,q.pathname); console.log(‘search = ‘.red,q.search); var mes = q.query.mes; var ano = q.query.ano; console.log(‘data = ‘.green,mes+’/’+ano); Figura 6 – Módulo URL. Fonte: Acervo pessoal. 3 - Módulo Sistema de Arquivos Até esse momento inseríamos o conteúdo HTML que o nosso servidor responde dentro do próprio servidor, mas uma boa prática é separar o que é HTML do JavaScript. O módulo nativo fs pode ser usado para manipular arquivos e diretórios. Com o módulo fs podemos ler um arquivo do sistema operacional utilizando as funções .readFile() ou .readFileSync(). A diferença entre elas é que uma das funções é síncrona e a outra assíncrona. Pereira (2014) esclarece que o .readFile() faz uma leitura assíncrona e “depois que o arquivo foi carregado, é invocada uma função call-back para fazer os tratamentos finais, seja de erro ou de retorno, do arquivo.” já o fs.readFileSync faz uma leitura síncrona “bloqueando a aplicação até terminar sua leitura e retornar o arquivo”. Antes de demonstrarmos o código do servidor com o módulo fs, precisamos criar o arquivo HTML, que será retornado quando alguma chamada for feita ao servidor. Daremos a esse arquivo o nome de index.html, com o seguinte conteúdo. <!DOCTYPE html> <html> <head> <title>Exemplo modulo fs</ title> </head> <body> <h1>Este arquivo foi chamado pelo modulo fs</h1> </body> </html> Primeiramente o módulo fs tem de ser carregado juntamente do http. E quando o cliente fizer uma solicitação ao servidor dentro do createServer() o arquivo será lido pela função readFile() e o resultado da abertura do arquivo será retornado para o cliente. const http = require(‘http’); 41 const fs = require(‘fs’); const server = http. createServer((request,response)=>{ var diretorio = __dirname; // retorna o diretório da aplicacao fs.readFile(diretorio+’/index. html’,(erro,html)=>{ r e s p o n s e . writeHeader(200,{‘Content-Type’:’text/ html’}); response.write(html); response.end(); }); }); server.listen(3000); Figura 7 – Módulo FS. Fonte: Acervo pessoal. Podemos usar os módulos url e fs para criar diversas rotas que darão em múltiplas páginas. O arquivo solicitado só retorna quando ele existe. Quando ele não for encontrado, volta o erro 404. const http = require(‘http’); const fs = require(‘fs’); const url = require(‘url’); const server = http. createServer((request,response)=>{ var diretorio = __dirname; var q = url.parse(request.url, true); f s . r e a d F i l e ( d i r e t o r i o + q . pathname,(erro,html)=>{ if(erro){ r e s p o n s e . writeHeader(404,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(“Pagina invalida”); response.end(); }else{ r e s p o n s e . writeHeader(200,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(html); response.end(); } }); }); server.listen(3000); Se a url estiver vazia quando for passada para o servidor, nenhum arquivo será encontrado, então, o servidor retorna “Página inválida” Figura 8. Quando a url ”http:// localhost:3000/index.html” possui o arquivo e este é valido, o servidor então retorna o conteúdo do arquivo, Figura 9. Quem vai determinar qual rota deve seguir no caso do código acima é se a tentativa de ler o arquivo ocasionou erro ou não. Figura 8 – Arquivo não encontrado. Fonte: Acervo pessoal. Figura 9 – Arquivo encontrado. Fonte: Acervo pessoal. 4 - Módulo Formulário Todo sistema web possui pelo menos um formulário, seja para cadastro de e-mail para receber notícias, ou formulários complexos para cadastro de produtos em um estoque. Para que o servidor receba as informações de um formulário, o primeiro passo é construí-lo em HTML e identificar as tags que serão preenchidas com a propriedade nome a que elas são relacionadas. Todos os elementos de um formulário estarão dispostos entre as tags de abertura e fechamento form <form></form>. Na tag de abertura duas propriedades devem ser setadas: a action, que será passada para o servidor no pathname, e o método, que colocaremos POST para que os dados sejam enviados sem que apareçam na url. Criaremos uma página externa chamada login.html que terá um formulário com dois campos de texto para serem digitados, um e-mail e uma senha. Desenvolvimento Voltado a Web III 42 <!DOCTYPE html> <html> <head> <title>Exemplo modulo formidable</title> </head> <body> <h1>Login</h1> <form action=”login” method=”post” enctype=”multipart/form-data”> <input type=”text” name=”login”> < i n p u t type=”password” name=”senha”> < i n p u t type=”submit”> </form> </body> </html> Figura 10 – Página de Login. Fonte: Acervo pessoal. Antes de desenvolvermos o servidor, precisamos instalar o módulo que auxilia na manipulação dos formulários, o formidable. Fazemos isso através do npm. npm install formidable --save Iremos utilizar o servidor criado na seção anterior para dar continuidade ao exemplo. Apenas criaremos uma rota diferente dentro do nosso código, para quando a reuqest receber na url o mesmo nome que foi colocado na propriedade action da tag form. As linhas acrescentadas estarão com o fundo amarelo. const http = require(‘http’); const fs = require(‘fs’); const url = require(‘url’); var formidable = require(‘formidable’); const server = http. createServer((request,response)=>{ if(request.url == “/login”){ var form = new formidable. IncomingForm(); f o r m . parse(request,(err,fields,files)=>{ i f ( f i e l d s . login==”felipe”&&fields.senha==”felipe”){ r e s p o n s e . writeHeader(200,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(fields.login+’ - ‘+fields.senha); r e s p o n s e . end(); }else{ r e s p o n s e . writeHeader(200,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(“Usuario ou senha incorretos!”); r e s p o n s e . end(); } }); }else{ var diretorio = __dirname; var q = url.parse(request. url, true); fs.readFile(diretorio+q. pathname,(erro,html)=>{ if(erro){ r e s p o n s e . writeHeader(404,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(“Pagina invalida”); r e s p o n s e . end(); }else{ r e s p o n s e . writeHeader(200,{‘Content-Type’:’text/ html’}); r e s p o n s e . write(html); r e s p o n s e . end(); } }); } }); server.listen(3000); Figura 11 – Formulário preenchido. Fonte: Acervo pessoal. 43 Figura 12 – Usuário ou senha incorretos. Fonte: Acervo pessoal. Figura 13 – Usuário e senha corretos. Fonte: Acervo pessoal. As alterações feitas possibilitam nosso servidor retornar mais duas possíveis rotas, quando o login e senha estão corretos e quando o login e senha estão incorretos. O módulo formidable, além de tratar dos formulários que enviam textos, ainda facilita manipulação de formulários mais complexos como o upload de arquivos para o servidor. 5 - Módulo E-mail Quando um usuário novo se cadastra em um site de compras, o site normalmente retorna um emailpara esse novo cliente com algumas informações relevantes para sua utilização. Esse e-mail é enviado pelo lado do servidor. O módulo nodemailer facilita o envio de e-mails a partir do servidor Node.js. Este também não é um módulo nativo e necessita da instalação via npm. npm install nodemailer --save Antes de iniciar a configuração do módulo em nosso Node.js precisamos descobrir se o nosso e-mail está habilitado para que envie e-mails externamente. No Gmail você pode configurar a sua conta para permitir acesso a apps com baixa segurança, https://www.google.com/settings/security/ lesssecureapps. Vamos então ao código do nodemailer. O primeiro passo é carregar o módulo, seguido da criação do transportador, que irá conter as informações para acessar a conta que enviará os e-mails. var nodemailer = require(‘nodemailer’); var transportador = nodemailer. createTransport({ service: ‘gmail’, auth: { user: ‘seuemail@gmail.com’, pass: ‘suasenha’ } }); Em seguida é possível montar a mensagem criando um objeto que terá valores atribuídos para as propriedades, from, to, subject e text que, traduzindo: de, para, assunto e conteúdo, respectivamente. var email = { from: ‘seuemail@gmail.com’, to: ‘destinatario@yahoo.com’, subject: ‘Enviando e-mail usando o Node.js’, text: ‘Exemplo de envio de e-mail utilizando o módulo nodemailer’ }; Para efetivar o envio é necessário que a função .sendMail() contendo o objeto email seja executada a partir do transportador que foi criado. transportador.sendMail(mailOptions, (error,info)=>{ if (error) { console.log(error); } else { console.log(‘Email enviado: ‘ + info.response); } }); Essa função é amplamente utilizada, e no exemplo do servidor poderíamos criar uma nova rota, para que quando um usuário realizasse o cadastro através de um formulário, recebesse um link por e-mail para confirmá-lo. Retomando a aula Chegamos ao final da quinta aula. Vamos recordar? 1 – Módulo HTTP Na primeira seção foi feita uma contextualização sobre o protocolo HTTP e como criar um servidor em Node.js, que responda a solicitações que possam ser feitas pelo lado do cliente. 2 – Módulo URL Na segunda seção, o módulo url foi detalhado. Ele é responsável por determinar o que queremos ver do web Desenvolvimento Voltado a Web III 44 server ou por enviar dados do lado do cliente para o lado do servidor. 3 – Módulo Sistema de Arquivos Em seguida, o módulo de sistema de arquivos incorporado no servidor em conjunto com o módulo url permitem a criação de diversas rotas dentro do servidor, além de separar o HTML do JavaScript, já que as páginas são criadas externamente e só serão retornadas pelo servidor quando algum usuário no lado do cliente fizer essa solicitação. 4 – Módulo Formulário O módulo formidable foi apresentado na quarta seção. Ele é responsável por gerenciar o que o servidor recebe quando um formulário é enviado. Pode trabalhar com entradas de texto e também com o envio de arquivos. 5 – Módulo Email Na última seção foi descrito o módulo nodemailer, que é capaz de enviar e-mails no lado do servidor. É muito útil na construção de funções de recuperação de senha, novos cadastros com confirmação por e-mail, entre muitos outros. LECHETA, R R. Node Essencial, Novatec, 2012. PEREIRA, C R. Aplicações web real-time com Node. js. Editora Casa do Código, 2014. RUBENS, J. Primeiros passos com Node.js. Editora Casa do Código, 2017. Vale a pena ler COPES, F. In: The Node. js Handbook, 2019. Disponível em: <https://flaviocopes.com/page/ ebooks/>. Acesso em: 20 mai. 2019. DELBONO, E. In: Node.js Succinctly, 2016. Disponível em: <https://www.syncfusion.com/ebooks/ nodejs>. Acesso em: 20 mai. 2019. NODE. In: Node.js Tutorial, 2019. Disponivel em: <https://www.w3schools.com/nodejs/default.asp>. Acesso em: 02 jun. 2019 Vale a pena acessar Vale a pena Minhas anotações