الفصل الثالث: تطوير الويب
النماذج والبيانات في Express
تعلم كيفية التعامل مع النماذج وإرسال البيانات من وإلى الخادم
أنواع طلبات HTTP
| الطريقة | الاستخدام | مثال |
|---|---|---|
| GET | جلب البيانات | عرض صفحة، قائمة مستخدمين |
| POST | إنشاء بيانات جديدة | تسجيل مستخدم، إضافة منتج |
| PUT | تحديث كامل | تحديث ملف مستخدم بالكامل |
| PATCH | تحديث جزئي | تحديث اسم المستخدم فقط |
| DELETE | حذف البيانات | حذف منتج، إلغاء حساب |
معالجة بيانات POST
1. إعداد Express لاستقبال البيانات:
const express = require('express');
const app = express();
// لمعالجة JSON
app.use(express.json());
// لمعالجة بيانات النماذج
app.use(express.urlencoded({ extended: true }));
app.listen(3000, () => {
console.log('Server running on port 3000');
});
2. استقبال بيانات POST:
// مثال: تسجيل مستخدم جديد
app.post('/register', (req, res) => {
const { username, email, password } = req.body;
// التحقق من البيانات
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: 'جميع الحقول مطلوبة'
});
}
// حفظ في قاعدة البيانات (مثال)
console.log('مستخدم جديد:', { username, email });
res.status(201).json({
success: true,
message: 'تم التسجيل بنجاح',
data: { username, email }
});
});
رفع الملفات
لرفع الملفات، نحتاج لمكتبة multer:
// تثبيت multer
pnpm add multer
// في الكود
const multer = require('multer');
// إعداد التخزين
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
});
// تصفية الملفات (مثلاً: صور فقط)
const fileFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('الملف ليس صورة!'), false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: { fileSize: 5 * 1024 * 1024 } // 5MB max
});
// استخدام في المسار
app.post('/upload', upload.single('image'), (req, res) => {
if (!req.file) {
return res.status(400).json({ message: 'لم يتم رفع ملف' });
}
res.json({
message: 'تم رفع الملف بنجاح',
filename: req.file.filename,
path: req.file.path,
size: req.file.size
});
});
// رفع ملفات متعددة
app.post('/upload-multiple', upload.array('images', 5), (req, res) => {
res.json({
message: `تم رفع ${req.files.length} ملف`,
files: req.files.map(f => f.filename)
});
});
التحقق من البيانات (Validation)
استخدم express-validator للتحقق من البيانات:
// تثبيت
pnpm add express-validator
// الاستخدام
const { body, validationResult } = require('express-validator');
app.post('/register',
// قواعد التحقق
[
body('username')
.trim()
.isLength({ min: 3, max: 20 })
.withMessage('اسم المستخدم يجب أن يكون بين 3-20 حرف')
.isAlphanumeric()
.withMessage('اسم المستخدم يجب أن يحتوي على أحرف وأرقام فقط'),
body('email')
.trim()
.isEmail()
.withMessage('البريد الإلكتروني غير صحيح')
.normalizeEmail(),
body('password')
.isLength({ min: 8 })
.withMessage('كلمة المرور يجب أن تكون 8 أحرف على الأقل')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('كلمة المرور يجب أن تحتوي على أحرف كبيرة وصغيرة وأرقام'),
body('age')
.optional()
.isInt({ min: 13, max: 120 })
.withMessage('العمر يجب أن يكون بين 13-120')
],
// معالج المسار
(req, res) => {
// فحص الأخطاء
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}
// البيانات صحيحة، تابع المعالجة
const { username, email, password, age } = req.body;
res.json({
success: true,
message: 'البيانات صحيحة',
data: { username, email, age }
});
}
);
أمان النماذج
نصائح أمنية مهمة
- لا تثق أبداً ببيانات المستخدم: تحقق دائماً من البيانات الواردة
- استخدم HTTPS: لتشفير البيانات أثناء الإرسال
- حماية من CSRF: استخدم tokens لحماية من Cross-Site Request Forgery
- تنظيف البيانات: أزل أي أكواد خبيثة (XSS)
- Rate Limiting: حدد عدد الطلبات لمنع الهجمات
مثال: حماية من CSRF
// تثبيت csurf
pnpm add csurf
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(require('cookie-parser')());
// عرض نموذج مع CSRF token
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// معالجة النموذج مع التحقق من CSRF
app.post('/process', csrfProtection, (req, res) => {
res.send('البيانات آمنة!');
});
مثال: Rate Limiting
// تثبيت
pnpm add express-rate-limit
const rateLimit = require('express-rate-limit');
// حد 100 طلب كل 15 دقيقة
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'تجاوزت الحد المسموح من الطلبات، حاول لاحقاً'
});
// تطبيق على جميع المسارات
app.use(limiter);
// أو على مسار محدد
app.post('/login', limiter, (req, res) => {
// ...
});
مثال شامل: نظام تسجيل
const express = require('express');
const { body, validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// قاعدة بيانات وهمية
const users = [];
// صفحة التسجيل (HTML)
app.get('/register', (req, res) => {
res.send(`
التسجيل
التسجيل
`); }); // معالجة التسجيل app.post('/register', [ body('username').trim().isLength({ min: 3 }).isAlphanumeric(), body('email').trim().isEmail().normalizeEmail(), body('password').isLength({ min: 8 }), body('age').optional().isInt({ min: 13 }) ], async (req, res) => { // فحص الأخطاء const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } const { username, email, password, age } = req.body; // التحقق من عدم وجود المستخدم const exists = users.find(u => u.email === email); if (exists) { return res.status(400).json({ message: 'البريد الإلكتروني مستخدم بالفعل' }); } // تشفير كلمة المرور const hashedPassword = await bcrypt.hash(password, 10); // حفظ المستخدم users.push({ id: users.length + 1, username, email, password: hashedPassword, age: age || null, createdAt: new Date() }); res.status(201).json({ success: true, message: 'تم التسجيل بنجاح', user: { username, email } }); } ); // عرض المستخدمين app.get('/users', (req, res) => { res.json({ count: users.length, users: users.map(u => ({ id: u.id, username: u.username, email: u.email, age: u.age })) }); }); app.listen(3000, () => { console.log('Server: http://localhost:3000'); });الخلاصة
- استخدم express.json() و express.urlencoded() لمعالجة البيانات
- multer لرفع الملفات
- express-validator للتحقق من البيانات
- لا تثق أبداً ببيانات المستخدم
- استخدم CSRF protection و Rate Limiting للأمان
- شفر كلمات المرور دائماً (bcrypt)