Criando aplicações web com Node.js charles.viegas@codate.com.br @charlesviegas Charles Viegas
Referências nodejs.org http://expressjs.com/ https://github.com/expressjs/body-parser https://github.com/expressjs/session Livro: Node.js: aplicações web real-time com Node.js Caio Ribeiro Casa do código
Código fonte da solução https://github.com/charlesviegas/minicurso-nodejs.git
introdução ao node.js
O que é o Node.js? Javascript no lado do servidor Construído sobre o engine V8 do Chrome Executa por linha de comando Projetado para alta concorrência Single thread Não bloqueante Utiliza o framework CommonsJS
Não bloqueia IO Esperar pedidos de I/O degrada a performance. O Node usa evento em JS para anexar retornos a chamadas de IO.
Exemplo BLOQUEANTE NÃO BLOQUEANTE ... String sql = "SELECT c FROM Contato"; Query q= em.createQuery(sql); List result = query.getResultList(); … var callback = function(err, rows) { if(err) throw err; console.log(rows); }); con.query("SELECT * FROM Contato", callback);
Event Loop Ao invés de threads, o Node usa um loop de eventos, aliviando o overhead da troca de contexto.
Instalação e Configuração Acesse o site http://nodejs.org Faça download do executável e instale de forma padrão Verifique se a instalação funcionou corretamente executando os comandos: node –version npm --version
Prática 1 - Olá Javaneiros Abra o terminal e navegue para a pasta: curso\pratica1 Execute o comando: node olajavaneiros.js Observe a saída no próprio terminal Execute o comando: node olajavaneiroshttp.js Abra seu navegador e acesse a url impressa no terminal
NPM Node Package Manager Baseado em linha de comando Gerencia os módulos do node, baixando da web os módulos e suas dependências Baseado em linha de comando npm install nome_do_modulo --save npm install – g nome_do_modulo npm remove nome_do_modulo npm update nome_do_modulo npm list
package.json O Node utiliza o arquivo package.json para descrever os metadados do projeto. Nome, versão, dependências Deve estar presente na pasta raiz de cada projeto.
package.json { "name": “nome_do_projeto", "description": "Meu primeiro app em Node.js", "author": “Charles Viegas <charles@email.com>", "version": "1.0.0", "private": true, "dependencies": { "modulo-1": "1.0.0", "modulo-2": "~1.0.0", "modulo-3": ">=1.0.0" }, "devDependencies": { "modulo-4": "*" }
Prática 2 - Criando o package.json Durante o curso iremos criar uma pequena aplicação para controlar uma lista de presentes para casamento. Crie a pasta raiz: Nome: casalista Crie dentro da pasta o arquivo: Nome: package.json Conteúdo: name, description, author, version e private Execute dentro da pasta raiz o seguinte comando: npm install
Prática 2 - Criando o package.json { "name": “casalista", "description": “App para lista de casamento em Node.js", "author": “Seu Nome <seuemail@email.com>", "version": "1.0.0", "private": true }
Objetos globais global: assim como nos browsers o Node também possui um contexto de alto nível. process: contém os dados do processo de execução. console: usado para imprimir mensagens nas saídas de fluxo padrões. Buffer: usado para manipular dados binários. require: usado para importação de módulos
Objetos globais __filename: o nome do arquivo que está sendo executado __dirname: o nome do diretório que reside o arquivo que está sendo executado. module: uma referência para o módulo corrente.
CommonJS O Node utiliza nativamente o padrão CommonJS para organização e carregamento dos módulos. var m2 = require(“./modulo2.js”); m2(“Ola Javaneiros”); modulo1.js module.exports = function(msg){ console.log(msg); } modulo2.js
dominando Express
O que é o Express? Escrever código usando a API HTTP do Node é muito trabalhoso. O Express é um módulo para desenvolvimento de aplicações web de grande escala.
Características do Express MVR (Model-View-Routes) MVC (Model-View-Controller) Roteamento de urls via callbacks Middleware Interface RESTFul Suporte a File Uploads Integração com Template Engines
Módulos do Express A partir da versão 4, o Express passou a ser distribuído em vários módulos, sendo eles: express: Módulo principal do express. express-load: Carregamento dinâmico de arquivos JS. express-session: Controle de sessões do usuário. method-override: Traduz método POST para PUT e DELETE. body-parser: Parser de campos do html para objeto JSON. cookie-parser: Parser de cookie para objeto JSON. ejs: Engine de template.
Prática 3 - Instalando o express Altere o seu package.json incluindo como dependência as últimas versões dos módulos: express, express-load, express-session, method-override, body-parser, cookie-parser e ejs Dentro da pasta raiz execute o comando: npm install O npm vai criar a pasta node_modules e dentro delas todos os seus módulos
Prática 4 – Criando as pastas do projeto Copie para a pasta raiz do seu projeto a estrutura de pastas e arquivos definida em curso\pratica4
Prática 4 – Criando as pastas do projeto controllers: vai conter os nossos controladores (MVC). routes: vai conter os nossos roteadores. public: vai conter os css e imagens. views: vai conter as nossas views (MVC). app.js: arquivo JS principal do nosso projeto. Atenção: por questões de simplificação não teremos o model (MVC) neste curso.
Iniciando um servidor Express // Importa e instancia um servidor express var express = require("express"), app = express(); // Configurações futuras // Sobe o servidor HTTP na posta 3000 app.listen(3000, function () { console.log(“Servidor está no ar."); });
Prática 5 – Iniciando um servidor express Conforme exemplo anterior altere o arquivo app.js Execute o comando: node app.js Abra o navegador no endereço: http://localhost:3000 Você deve receber a mensagem: Cannot GET /
Definindo views // Define a pasta onde irão ficar as views ... // Configurações futuras // Define a pasta onde irão ficar as views app.set("views", __dirname + "/views"); // Define o template engine usado nas views app.set("view engine", "ejs");
Definindo conteúdo estático // Configurações futuras ... // Define a pasta public para conteúdo estático app.use(express.static(__dirname + "/public"));
Definindo as rota inicial // Configurações futuras ... // Responde ao acessar o localhost:3000/ app.use("/", function(req, res){ res.send("Ola Javaneiros"); });
Prática 6 – Definindo views, conteúdo estático e rota inicial Altere o arquivo app.js com os passos feitos nos últimos três slides. Reinicie o node visualizar as alterações. Para ver os resultados, abra seu navegador no endereço http://localhost:3000/ Para testar o conteúdo estático abra também o endereço http://localhost:3000/app/css/style.css
Express-load Utilizaremos o express-load que carrega modulos dentro da instância do express simplementes informando a pasta onde estas scripts estão. Portanto, ao invés de usar o require(“nome_do_modulo”), nós utilizaremos o express-load para esta função.
Express-load // Importa o módulo express-load var express = require("express"), load = require("express-load"), app = express(); ... // Carrega todas as scripts da pasta controller e routes load("controllers") .then("routes") .into(app);
Prática 7 – Express-load Adicione o express-load conforme slides anteriores. Reinicie o node visualizar as alterações.
Criando Roteadores É comum que os roteamento no express fiquem separados em módulos externos para melhorar a legibilidade do código. Portanto iremos remover o código abaixo da app.js: app.use("/", function(req, res){ res.send("Ola Javaneiros"); });
Criando Roteadores E criaremos um módulo a parte só para roteamento. module.exports = function (app) { var home = app.controllers.home; app.get("/", home.index); app.post("/entrar", home.entrar); };
Criando Controladores Os controladores controlam a sequência de execução e a navegação entre as páginas. Também é recomendável a separação da sua lógica num módulo separado. module.exports = function (app) { var HomeController = { index: function (req, res) { res.render("home"); }, entrar: function (req, res) { } }; return HomeController; };
Prática 8 – Roteadores e Controladores Crie os roteadores e controladores conforme slides anteriores.
Criando views com EJS Para criação das Views utilizaremos o EJS. O EJS permite imprimir o conteúdo de objetos JSON em partes do html Também permite a inclusão de loops, estruturas condições e includes
Criando views com EJS Organizaremos nosso código da seguinte forma: header.ejs footer.ejs home.ejs
header.ejs <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Javaneiros - Lista de presentes de casamento</title> <link rel="stylesheet" href="/bootstrap/css/bootstrap.css"/> <link rel="stylesheet" href="/bootstrap/css/bootstrap-theme.css"/> <link rel="stylesheet" href="/app/css/style.css" /> </head> <body>
footer.ejs <footer class="navbar navbar-default navbar-fixed-bottom"> <div class="container"> Javaneiros 2014 </div> </footer> <script src="/bootstrap/js/jquery.js"></script> <script src="/bootstrap/js/bootstrap.js"></script> </body> </html>
home.ejs <% include ./header %> <div class="container"> <form action="/entrar" method="post" role="form" class="form-signin"> <h2>Lista de presentes</h2> <input type="text" name="noivo" placeholder="Noivo" class="form-control" required="required" autofocus="autofocus"/> <input type="text" name="noiva" placeholder="Noiva" class="form-control" required="required"/> <button type="submit" class="btn btn-lg btn-primary btn-block">Ver lista</button> </form> </div> <% include ./footer %>
Prática 9 – Views Crie as views header, footer e home conforme slides anteriores.
Body-parser O express utiliza o modulo body-parser para realizar a conversão dos dados submetidos por um formulário html em objeto JSON.
Body-parser var express = require("express"), bodyParser = require("body-parser"), load = require("express-load"), app = express(); … // Inclui o parser para formato json app.use(bodyParser.json()); // Inclui parser para urlencoded app.use(bodyParser.urlencoded({extended: true})); ...
Express-session Recurso que permite manter dados do usuário no servidor. Para acessar o objeto a ser mantido na sessão basta acessar req.session
Express-session var express = require("express"), bodyParser = require("body-parser"), load = require("express-load"), expressSession = require("express-session"), app = express(); … app.use(expressSession({ secret: "keyboard cat", resave: false, saveUninitialized: true })); ...
Prática 10 – Body-parser e Session Adicione o body-parser e o express-session conforme slides anteriores.
Manipulando session Agora iremos gravar na sessão do usuário os dados submetidos pela tela de home. Para isso, iremos alterar o controlador do home.
Manipulando Session ... var HomeController = { ... entrar: function (req, res) { var lista = { noivo : req.body.noivo, noiva : req.body.noiva, presentes : [] }; req.session.lista = lista; res.redirect("/presentes"); } };
Prática 11 – Manipulando session Complete o código do HomeController conforme slide anterior. Após submeter o formulário você verá a seguinte mensagem: Cannot GET /presentes
routers\presente.js module.exports = function (app) { var presentes = app.controllers.presentes; app.get("/presentes", presentes.index); app.get("/comprar/:id", presentes.comprar); app.post("/presentes", presentes.incluir); };
controllers\presente.js module.exports = function (app) { var PresentesController = { index: function (req, res) { res.render("presentes", {presentes: req.session.lista.presentes}); }, incluir: function (req, res) { var lista = req.session.lista; var presente = { nome: req.body.nome, valor: req.body.valor }; lista.presentes.push(presente); res.redirect("/presentes"); }, comprar: function (req, res) { var id = req.params.id; req.session.lista.presentes[id].comprado = true; res.redirect("/presentes"); } }; return PresentesController; };
view\presentes.ejs <% include ./header %> <div class="container"> <div class="page-header"> <h1>Lista de presentes</h1> </div> <div class="row"> <!– ****************** Formulário --> </div> <div class="row"> <!– ****************** Tabela --> </div> <div class="row"> <a href="/">Sair</a> </div> </div> <% include ./footer %>
view\presentes.ejs - formulário <div class="col-xs-12"> <form action="/presentes" method="post" role="form"> <div class="row"> <div class="col-xs-4"> <input type="text" name=“nome" placeholder="Presente" class="form-control"/> </div> <div class="col-xs-4"> <input type="text" name=“valor" placeholder="Valor" class="form-control"/> </div> <div class="col-xs-4"> <button type="submit" class="btn btn-default">Incluir</button> </div> </div> </form> </div>
view\presentes.ejs - tabela <div class="col-xs-12"> <table class="table table-bordered table-striped"> <thead><tr> <th>Presente</th> <th>Valor</th><th>Ação</th> </tr></thead> <tbody> <% presentes.forEach(function(presente, index) { %> <tr> <td><%- presente.nome %></td> <td><%- presente.valor %></td> <td> <% if (!presente.comprado) {%> <a href="/comprar/<%- index %>">Comprar</a> <%}%> </td> </tr> <% }) %> </tbody> </table> </div>
Prática 12 – Final Crie o roteador, controlador e a view para a tela de presentes conforme slides anteriores.