Всё о web

Для чайников и не только


Создаём REST API сервер на Hapi часть 3: Валидация запросов

Декабрь 16, 2018

Валидация реквестов нужна для того, чтобы наши доблестные пользователи не слали всякую херню, да и чтобы хацкерам было сложнее найти уязвимости.

Родным для hapi модулем валидации схем данных является Joi, а сама схема данных описывается внутри рута:

{
  method: 'GET',
  path: '/',
  options: {
    handler: response,
    validate: {
      payload: {}, // Проверяем пост параметры
      query: {}, // проверяем гет параметры
      params: {} // проверяем path параметры
    }
  }
}

Для примера, добавим новый роут:

// ./src/routes/messages/get_single.js

const Joi = require('joi');
const Boom = require('boom');

async function response(request) {

  const messages = request.getModel(request.server.config.db.database, 'messages');
  let data = await messages.findOne({ where: { id: request.params.id } });

  if( !data ) { // Записи с таким ID нет
    throw Boom.notFound();
  }

  let count = await messages.count();

  return {
    meta: {
      total: count
    },
    data: [ data ]
  };
}

module.exports = {
  method: 'GET',
  path: '/messages/{id}',
  options: {
    handler: response,
    validate: {
      params: {
        id: Joi.number().integer().required() // ID должен быть, целым числом, параметр обязателен
      }
    }
  }
};

Проверяем правильным запросом:

frfr@lenochka:~$ curl http://127.0.0.1:3030/messages/1
{"meta":{"total":2},"data":[{"id":1,"user_id":1,"message":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat","createdAt":"2018-12-16T16:44:01.426Z","updatedAt":"2018-12-16T16:44:01.426Z"}]}

Несуществующая запись:

frfr@lenochka:~$ curl http://127.0.0.1:3030/messages/3
{"statusCode":404,"error":"Not Found","message":"Not Found"}

И теперь самое интересное, запись с неверным параметром:

frfr@lenochka:~$ curl http://127.0.0.1:3030/messages/asd
{"statusCode":400,"error":"Bad Request","message":"Invalid request params input"}

Теперь добавим POST роут, для тех же сообщений:

// ./src/routes/messages/post.js

const Joi = require('joi');

async function response(request) {

  const messages = request.getModel(request.server.config.db.database, 'messages');
  let newMessage = await messages.create(request.payload); // Прям фигачим пост дату сразу в ОРМ

  let count = await messages.count();

  return {
    meta: {
      total: count
    },
    data: [ newMessage ]
  };
}

module.exports = {
  method: 'POST',
  path: '/messages',
  options: {
    handler: response,
    validate: {
      payload: { // Потомучто пост дата прошла валидацию
        user_id: Joi.number().integer().required(),
        message: Joi.string().min(1).max(100).required()
      }
    }
  }
};

Проверяем, валидный реквест:

frfr@lenochka:~$ curl -H "Content-Type: application/json" -X POST -d '{"user_id":1,"message":"Preved Medved"}' http://127.0.0.1:3030/messages
{"meta":{"total":3},"data":[{"id":3,"user_id":1,"message":"Preved Medved","updatedAt":"2018-12-16T17:11:02.958Z","createdAt":"2018-12-16T17:11:02.958Z"}]}

И парочку не валидных:

// С пустым message
frfr@lenochka:~$ curl -H "Content-Type: application/json" -X POST -d '{"user_id":1,"message":""}' http://127.0.0.1:3030/messages
{"statusCode":400,"error":"Bad Request","message":"Invalid request payload input"}

// С дополнительным параметром
frfr@lenochka:~$ curl -H "Content-Type: application/json" -X POST -d '{"user_id":1,"message":"Ololo", "id": 1}' http://127.0.0.1:3030/messages
{"statusCode":400,"error":"Bad Request","message":"Invalid request payload input"}

// Без требуемого параметра
frfr@lenochka:~$ curl -H "Content-Type: application/json" -X POST -d '{"message":"Ololo"}' http://127.0.0.1:3030/messages
{"statusCode":400,"error":"Bad Request","message":"Invalid request payload input"}

Валидировать можно очень сложные схемы, например, массив объектов:

/*
[
  {
    id: 1,
    any_param: true,
    any_param2: "asd"
  },
  {
    id: 2,
    any_param: false,
    any_param2: "qwe"
  }
  ...
]
*/
Joi.array().items( Joi.object({
  id: Joi.number().integer(),
  any_param: Joi.boolean().default(false),
  any_param2: Joi.string()
}))

Исходники на гитхабе: https://github.com/hololoev/api_hapi_example_3



Комментарии

Оставить комментарий:

Ваш e-mail не будет опубликован. Обязательные поля помечены *