для эффективной обработки ошибок необходимо использовать промежуточное ПО
создайте middleware/error.js и добавьте
const errorHandler = (err, req, res, next) => { res.status(500).json({ sucess:false, error:err.message }); } module.exports = errorHandler;
нам просто нужно сделать 2 настройки в server.js, чтобы включить файл и использовать его
const express = require('express'); const dotenv = require('dotenv'); const morgan = require('morgan'); const connectDB = require('./config/db'); const errorHandler = require('./middleware/error'); // Load env vars dotenv.config({ path: './config/config.env' }); //connect to db connectDB(); // Route files const bootcamps = require('./routes/bootcamps'); const app = express(); //Body parser app.use(express.json()); // dev logging middleware if (process.env.NODE_ENV == 'development') { app.use(morgan('dev')); } // Mount routers app.use('/api/v1/bootcamps', bootcamps); app.use(errorHandler); const PORT = process.env.PORT || 5000; const server = app.listen( PORT, console.log(`server running in ${process.env.NODE_ENV} mode on PORT ${PORT}`) ); //handle unhandled promise rejection process.on('unhandledRejection', (err, promise) => { console.log(`Error: ${err.message}`); //console.log('dsds'); // Close server & exit process //server.close(() => process.exit(1)); }); //mongodb://localhost:27017/dev-learn
обновите controllers/bootcamps.js и добавьте
exports.getBootcamp = async (req,res,next) => { try { const bootcamp = await Bootcamp.findById(req.params.id); if(!bootcamp){ return res.status(400).json({sucess:false}); } res.status(200).json({success:true,data:bootcamp}); } catch (err) { //res.status(400).json({sucess:false}); next(err); } };
вот как у нас есть середина для отображения сообщений об ошибках
теперь мы можем больше настраивать наше сообщение, используя наследование и изменяя содержимое нашего сообщения.
создайте utils/errorResponse.js и добавьте
class ErrorResponse extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; //Error.captureStackTrace(this, this.constructor); } } module.exports = ErrorResponse;
обновить middleware/error.js с помощью
const errorHandler = (err, req, res, next) => { res.status(err.statusCode || 500).json({ sucess:false, error:err.message || 'Server error' }); }; module.exports = errorHandler;
и controllers/bootcamps.jsмодуль getbootcamp с
exports.getBootcamp = async (req,res,next) => { try { const bootcamp = await Bootcamp.findById(req.params.id); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({success:true,data:bootcamp}); } catch (err) { //res.status(400).json({sucess:false}); next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } };
мы можем добавить больше настроек, поэтому обновите middleware/error.js с помощью
const errorHandler = (err, req, res, next) => { let error = { ...err }; error.message = err.message; //Mongoose bad ObjectId if (err.name === 'CastError') { const message = `Resource not found ${err.value}`; error = new ErrorResponse(message, 404); } // // Mongoose duplicate key if (err.code === 11000) { const message = 'Duplicate field value entered'; error = new ErrorResponse(message, 400); } // // Mongoose validation error if (err.name === 'ValidationError') { const message = Object.values(err.errors).map(val => val.message); error = new ErrorResponse(message, 400); } res.status(error.statusCode || 500).json({ sucess:false, error: error.message || 'Server error' }); };
и все вместе с controllers/bootcamps.js
const Bootcamp = require('../models/Bootcamp'); const ErrorResponse = require('../utils/errorResponse'); exports.getBootcamps = async (req,res,next) => { try { const bootcamps = await Bootcamp.find(); res.status(200).json({success:true,count:bootcamps.length,data:bootcamps}); //next(err); } catch (err) { next(err); } }; exports.getBootcamp = async (req,res,next) => { try { const bootcamp = await Bootcamp.findById(req.params.id); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({success:true,data:bootcamp}); } catch (err) { //res.status(400).json({sucess:false}); next(err); } }; exports.createBootcamp = async (req,res,next) => { try{ const bootcamp = await Bootcamp.create(req.body); res.status(201).json({ success:true, data:bootcamp }); } catch (err){ next(err); } }; exports.updateBootcamp = async (req,res,next) => { try{ const bootcamp = await Bootcamp.findByIdAndUpdate(req.params.id,req.body,{ new:true, runValidators:true }); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({sucess:true,data:bootcamp}); } catch(err){ next(err); } }; exports.deleteBootcamp = async (req,res,next) => { try{ const bootcamp = await Bootcamp.findByIdAndDelete(req.params.id); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({sucess:true,data:{}}); } catch(err){ next(err); } };
СУХОЙ (Не повторяйтесь)
так что в наших контроллерах/bootcamps.js у нас везде один и тот же блок catch, можем ли мы что-то с этим сделать? да
создайте новый файл в middleware/async.js и добавьте
const asyncHandler = fn => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); module.exports = asyncHandler;
а затем controller/bootcamps.js замените код на
const Bootcamp = require('../models/Bootcamp'); const asyncHandler = require('../middleware/async'); const ErrorResponse = require('../utils/errorResponse'); exports.getBootcamps = asyncHandler(async (req, res, next) => { const bootcamps = await Bootcamp.find(); res.status(200).json({success:true,count:bootcamps.length,data:bootcamps}); //next(err); }); exports.getBootcamp = asyncHandler(async (req,res,next) => { const bootcamp = await Bootcamp.findById(req.params.id); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({success:true,data:bootcamp}); }); exports.createBootcamp = asyncHandler(async (req,res,next) => { const bootcamp = await Bootcamp.create(req.body); res.status(201).json({ success:true, data:bootcamp }); }); exports.updateBootcamp = asyncHandler(async (req,res,next) => { const bootcamp = await Bootcamp.findByIdAndUpdate(req.params.id,req.body,{ new:true, runValidators:true }); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({sucess:true,data:bootcamp}); }); exports.deleteBootcamp = asyncHandler(async (req,res,next) => { const bootcamp = await Bootcamp.findByIdAndDelete(req.params.id); if(!bootcamp){ return next(new ErrorResponse(`Bootcamp not found with id of ${req.params.id}`,400)); } res.status(200).json({sucess:true,data:{}}); });
Слагификация мангуста
npm i slugify
добавление слагов к нашему заголовку необходимо для улучшения SEO
перейдите в models/Bootcamp.js и добавьте это выше
const slugify = require('slugify');
а это внизу
// Create bootcamp slug from the name BootcampSchema.pre('save', function(next) { this.slug = slugify(this.name, { lower: true }); next(); });
Местоположение MapQuest API GeoJSON
в основном используется для указания полей местоположения, которые мы добавили в нашу модель
создайте свою учетную запись на https://developer.mapquest.com/
npm i node-geocoder
получить ключи от
https://developer.mapquest.com/user/me/apps
в config/config.env
GEOCODER_PROVIDER=mapquest GEOCODER_API_KEY=yourconsumerkey
создайте utils/geocoder.js и добавьте
const NodeGeocoder = require('node-geocoder'); const options = { provider: process.env.GEOCODER_PROVIDER, httpAdapter: 'https', apiKey: process.env.GEOCODER_API_KEY, formatter: null }; const geocoder = NodeGeocoder(options); module.exports = geocoder;
и в models/Bootcamp.js
добавьте это в строку выше
const geocoder = require('../utils/geocoder');
и это в строке ниже
// Geocode & create location field BootcampSchema.pre('save', async function(next) { const loc = await geocoder.geocode(this.address); this.location = { type: 'Point', coordinates: [loc[0].longitude, loc[0].latitude], formattedAddress: loc[0].formattedAddress, street: loc[0].streetName, city: loc[0].city, state: loc[0].stateCode, zipcode: loc[0].zipcode, country: loc[0].countryCode }; // Do not save address in DB this.address = undefined; next(); });
и в следующий раз, когда вы сделаете любой запрос на создание, также будет поле местоположения
создать новый буткемп с этими данными
{ "name": "ModernTech Bootcamp", "description": "ModernTech has one goal, and that is to make you a rockstar developer and/or designer with a six figure salary. We teach both development and UI/UX", "website": "https://moderntech.com", "phone": "(222) 222-2222", "email": "[email protected]", "address": "220 Pawtucket St, Lowell, MA 01854", "careers": ["Web Development", "UI/UX", "Mobile Development"], "housing": false, "jobAssistance": true, "jobGuarantee": false, "acceptGi": true }
Это все на данный момент