Используя Express и Node.js, мы можем реализовать полноценный API в стиле REST для взаимодействия с пользователем. Архитектура REST предполагает применение следующих методов или типов запросов HTTP для взаимодействия с сервером:
GET
POST
PUT
DELETE
Зачастую REST-стиль особенно удобен при создании всякого рода Single Page Application, которые нередко используют специальные javascript-фреймворки типа Angular, React или Knockout.
Рассмотрим, как создать свой API. Для нового проекта создадим новую папку, которая пусть будет называться webapp. Сразу определим в проекте файл package.json:
В проекте нам понадобятся express и body-parser для парсинга полученных данных.
Далее перейдем к этому каталогу в командной строке/терминале и для добавления всех нужных пакетов выполним команду:
1
npm install
В данном случае мы создадим экспериментальный проект, который будет хранить данные в файле json и который призван просто показать создание API в Node.js в стиле REST. А пока добавим в папку проекта новый файл users.json со следующим содержанием:
varexpress=require('express');varbodyParser=require('body-parser');varfs=require('fs');varapp=express();varjsonParser=bodyParser.json();app.use(express.static(__dirname+'/public'));// получение списка данныхapp.get('/api/users',function(req,res){varcontent=fs.readFileSync('users.json','utf8');varusers=JSON.parse(content);res.send(users);});// получение одного пользователя по idapp.get('/api/users/:id',function(req,res){varid=req.params.id;// получаем idvarcontent=fs.readFileSync('users.json','utf8');varusers=JSON.parse(content);varuser=null;// находим в массиве пользователя по idfor(vari=0;i<users.length;i++){if(users[i].id==id){user=users[i];break;}}// отправляем пользователяif(user){res.send(user);}else{res.status(404).send();}});// получение отправленных данныхapp.post('/api/users',jsonParser,function(req,res){if(!req.body)returnres.sendStatus(400);varuserName=req.body.name;varuserAge=req.body.age;varuser={name:userName,age:userAge};vardata=fs.readFileSync('users.json','utf8');varusers=JSON.parse(data);// находим максимальный idvarid=Math.max.apply(Math,users.map(function(o){returno.id;}));// увеличиваем его на единицуuser.id=id+1;// добавляем пользователя в массивusers.push(user);vardata=JSON.stringify(users);// перезаписываем файл с новыми даннымиfs.writeFileSync('users.json',data);res.send(user);});// удаление пользователя по idapp.delete('/api/users/:id',function(req,res){varid=req.params.id;vardata=fs.readFileSync('users.json','utf8');varusers=JSON.parse(data);varindex=-1;// находим индекс пользователя в массивеfor(vari=0;i<users.length;i++){if(users[i].id==id){index=i;break;}}if(index>-1){// удаляем пользователя из массива по индексуvaruser=users.splice(index,1)[0];vardata=JSON.stringify(users);fs.writeFileSync('users.json',data);// отправляем удаленного пользователяres.send(user);}else{res.status(404).send();}});// изменение пользователяapp.put('/api/users',jsonParser,function(req,res){if(!req.body)returnres.sendStatus(400);varuserId=req.body.id;varuserName=req.body.name;varuserAge=req.body.age;vardata=fs.readFileSync('users.json','utf8');varusers=JSON.parse(data);varuser;for(vari=0;i<users.length;i++){if(users[i].id==userId){user=users[i];break;}}// изменяем данные у пользователяif(user){user.age=userAge;user.name=userName;vardata=JSON.stringify(users);fs.writeFileSync('users.json',data);res.send(user);}else{res.status(404).send(user);}});app.listen(3000,function(){console.log('Сервер ожидает подключения...');});
Для обработки запросов определено пять методов для каждого типа запросов: app.get()/app.post()/app.delete()/app.put()
Когда приложение получает запрос типа GET по адресу api/users, то срабатывает следующий метод:
В качестве результата обработки мы должны отправить массив пользователей, которые считываем из файла. Для упрощения кода приложения в рамках данного экспериментального проекта для чтения/записи файла применяются синхронные методы fs.readFileSync()/fs.writeFileSync(). Но в реальности, как правило, работа с данными будет идти через базу данных, а далее мы все это рассмотрим на примере MongoDB.
И чтобы получить данные из файла с помощью метода fs.readFileSync() считываем данные в строку, которую парсим в массив объектов с помощью функции JSON.parse(). И в конце полученные данные отправляем клиенту методом res.send().
Аналогично работает другой метод app.get(), который срабатывает, когда в адресе указан id пользователя:
1 2 3 4 5 6 7 8 910111213141516171819
app.get('/api/users/:id',function(req,res){varid=req.params.id;// получаем idvarcontent=fs.readFileSync('users.json','utf8');varusers=JSON.parse(content);varuser=null;// находим в массиве пользователя по idfor(vari=0;i<users.length;i++){if(users[i].id==id){user=users[i];break;}}// отправляем пользователяif(user){res.send(user);}else{res.status(404).send();}});
Единственное, что в этом случае нам надо найти нужного пользователя по id в массиве, а если он не был найден, возвратить статусный код 404: res.status(404).send().
При получении запроса методом POST нам надо применить парсер jsonParser для извлечения данных из запроса:
// получение отправленных данныхapp.post('/api/users',jsonParser,function(req,res){if(!req.body)returnres.sendStatus(400);varuserName=req.body.name;varuserAge=req.body.age;varuser={name:userName,age:userAge};vardata=fs.readFileSync('users.json','utf8');varusers=JSON.parse(data);// находим максимальный idvarid=Math.max.apply(Math,users.map(function(o){returno.id;}));// увеличиваем его на единицуuser.id=id+1;// добавляем пользователя в массивusers.push(user);vardata=JSON.stringify(users);// перезаписываем файл с новыми даннымиfs.writeFileSync('users.json',data);res.send(user);});
После получения данных нам надо создать новый объект и добавить его в массив объектов. Для этого считываем данные из файла, добавляем в массив новый объект и перезаписываем файл с обновленными данными.
При удалении производим похожие действия, только теперь извлекаем из массива удаляемый объект и опять же перезаписываем файл:
1 2 3 4 5 6 7 8 91011121314151617181920212223
app.delete('/api/users/:id',function(req,res){varid=req.params.id;vardata=fs.readFileSync('users.json','utf8');varusers=JSON.parse(data);varindex=-1;// находим индекс пользователя в массивеfor(vari=0;i<users.length;i++){if(users[i].id==id){index=i;break;}}if(index>-1){// удаляем пользователя из массива по индексуvaruser=users.splice(index,1)[0];vardata=JSON.stringify(users);fs.writeFileSync('users.json',data);// отправляем удаленного пользователяres.send(user);}else{res.status(404).send();}});
Если объект не найден, возвращаем статусный код 404.
Если приложению приходит PUT-запрос, то он обрабатывается методом app.put(), в котором с помощью jsonParser получаем измененные данные:
Здесь также для поиска изменяемого объекта считываем данные из файла, находим изменяемого пользователя по id, изменяем у него свойства и сохраняем обновленные данные в файл.
Таким образом, мы определили простейший API. Теперь добавим код клиента. Итак, как установлено в коде, Express для хранения статических файлов использует папку public, поэтому создадим в проекте подобную папку. В этой папке определим новый файл index.html, который будет выполнять роль клиента. В итоге весь проект будет выглядеть следующим образом:
<!DOCTYPE html><html><head><metacharset="utf-8"/><metaname="viewport"content="width=device-width"/><title>Список пользователей</title><linkhref="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"rel="stylesheet"/><scriptsrc="https://code.jquery.com/jquery-2.2.4.min.js"></script></head><body><h2>Список пользователей</h2><formname="userForm"><inputtype="hidden"name="id"value="0"/><divclass="form-group"><labelfor="name">Имя:</label><inputclass="form-control"name="name"/></div><divclass="form-group"><labelfor="age">Возраст:</label><inputclass="form-control"name="age"/></div><divclass="panel-body"><buttontype="submit"class="btn btn-sm btn-primary">
Сохранить
</button><aid="reset"class="btn btn-sm btn-primary">Сбросить</a></div></form><tableclass="table table-condensed table-striped table-bordered"><thead><tr><th>Id</th><th>Имя</th><th>возраст</th><th></th></tr></thead><tbody></tbody></table><script>// Получение всех пользователейfunctionGetUsers(){$.ajax({url:'/api/users',type:'GET',contentType:'application/json',success:function(users){varrows='';$.each(users,function(index,user){// добавляем полученные элементы в таблицуrows+=row(user);});$('table tbody').append(rows);},});}// Получение одного пользователяfunctionGetUser(id){$.ajax({url:'/api/users/'+id,type:'GET',contentType:'application/json',success:function(user){varform=document.forms['userForm'];form.elements['id'].value=user.id;form.elements['name'].value=user.name;form.elements['age'].value=user.age;},});}// Добавление пользователяfunctionCreateUser(userName,userAge){$.ajax({url:'api/users',contentType:'application/json',method:'POST',data:JSON.stringify({name:userName,age:userAge,}),success:function(user){reset();$('table tbody').append(row(user));},});}// Изменение пользователяfunctionEditUser(userId,userName,userAge){$.ajax({url:'api/users',contentType:'application/json',method:'PUT',data:JSON.stringify({id:userId,name:userName,age:userAge,}),success:function(user){reset();$("tr[data-rowid='"+user.id+"']").replaceWith(row(user));},});}// сброс формыfunctionreset(){varform=document.forms['userForm'];form.reset();form.elements['id'].value=0;}// Удаление пользователяfunctionDeleteUser(id){$.ajax({url:'api/users/'+id,contentType:'application/json',method:'DELETE',success:function(user){console.log(user);$("tr[data-rowid='"+user.id+"']").remove();},});}// создание строки для таблицыvarrow=function(user){return("<tr data-rowid='"+user.id+"'><td>"+user.id+'</td>'+'<td>'+user.name+'</td> <td>'+user.age+'</td>'+"<td><a class='editLink' data-id='"+user.id+"'>Изменить</a> | "+"<a class='removeLink' data-id='"+user.id+"'>Удалить</a></td></tr>");};// сброс значений формы$('#reset').click(function(e){e.preventDefault();reset();});// отправка формы$('form').submit(function(e){e.preventDefault();varid=this.elements['id'].value;varname=this.elements['name'].value;varage=this.elements['age'].value;if(id==0)CreateUser(name,age);elseEditUser(id,name,age);});// нажимаем на ссылку Изменить$('body').on('click','.editLink',function(){varid=$(this).data('id');GetUser(id);});// нажимаем на ссылку Удалить$('body').on('click','.removeLink',function(){varid=$(this).data('id');DeleteUser(id);});// загрузка пользователейGetUsers();</script></body></html>
Основная логика здесь заключена в коде javascript. Для упрощения взаимодействия с сервером здесь применяется библиотека jquery. При загрузке страницы в браузере получаем все объекты из БД с помощью функции GetUsers:
1 2 3 4 5 6 7 8 9101112131415
functionGetUsers(){$.ajax({url:'/api/users',type:'GET',contentType:'application/json',success:function(users){varrows='';$.each(users,function(index,user){// добавляем полученные элементы в таблицуrows+=row(user);});$('table tbody').append(rows);},});}
Для добавления строк в таблицу используется функция row(), которая возвращает строку. В этой строке будут определены ссылки для изменения и удаления пользователя.
Ссылка для изменения пользователя с помощью функции GetUser() получает с сервера выделенного пользователя:
И выделенный пользователь добавляется в форму над таблицей. Эта же форма применяется и для добавления объекта. С помощью скрытого поля, которое хранит id пользователя, мы можем узнать, какое действие выполняется - добавление или редактирование. Если id равен 0, то выполняется функция CreateUser, которая отправляет данные в POST-запросе: