การสร้างมิดเดิลแวร์ Expressjs ที่ยอมรับพารามิเตอร์


110

ฉันกำลังพยายามสร้างมิดเดิลแวร์ที่สามารถรับพารามิเตอร์ได้ จะทำได้อย่างไร?

ตัวอย่าง

app.get('/hasToBeAdmin', HasRole('Admin'), function(req,res){

})

HasRole = function(role, req, res, next){
   if(role != user.role){
      res.redirect('/NotInRole);
   }

   next();
}

43
ฮ่าคุณถามคำถามที่แน่นอนของฉันสำหรับสถานการณ์ที่แน่นอนของฉัน แต่เมื่อ 6 ปีก่อนหน้านี้ น่ากลัวมาก
aero

6
@aero สิ่งเดียวกับที่ฉันกำลังมองหา: D
Jeson Dias

4
@aero 7 ปีต่อมาฉันกำลังมองหาสิ่งที่เหมือนกัน: D
jean d'arme

4
@aero 7+ ปีต่อมาฉันกำลังมองหาสิ่งเดียวกัน!
sidd

4
@aero 8 ~ หลายปีต่อมาฉันกำลังมองหาสิ่งเดียวกัน! \ o /
Ítalo Sousa

คำตอบ:


167
function HasRole(role) {
  return function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  }
}

ฉันต้องการตรวจสอบด้วยว่าฉันไม่ได้ทำสำเนาฟังก์ชันเดียวกันหลายชุด:

function HasRole(role) {
  return HasRole[role] || (HasRole[role] = function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  })
}

9
วิธีที่สองจะเก็บพารามิเตอร์ซึ่งเป็นพฤติกรรมที่ต้องการหรือไม่ก็ได้
Pier-Luc Gendreau

1
@ โจนาธานองค์ช่วยอธิบายนิยามที่สองของฟังก์ชันได้ไหม เกิดอะไรขึ้นที่นั่น? ฉันไม่เข้าใจบรรทัดต่อไปนี้ส่งคืน HasRole [บทบาท] || (HasRole [role] = function (req, res, next) {
Rafay Hassan

1
@JonathanOng คุณช่วยอธิบายตัวอย่างที่สองอีกหน่อยสำหรับคนที่ไม่รู้ว่าโหนดนั้นดีไหม (รวมฉันด้วย). เมื่อใดที่คุณจะได้รับสำเนาหลายชุดของฟังก์ชันเดียวกันและเมื่อใดที่อาจทำให้เกิดปัญหา ขอบคุณ.
Dave

โดยไม่ต้องแคชapp.get('/a', hasRole('admin'))และapp.get('/b', hasRole('admin'))จะสร้างการปิดใหม่สำหรับแต่ละhasRoleรายการ สิ่งนี้ไม่สำคัญเกินความเป็นจริงมากเกินไปเว้นแต่คุณจะมีแอปพลิเคชันขนาดใหญ่จริงๆ ฉันแค่เขียนโค้ดแบบนี้โดยค่าเริ่มต้น
Jonathan Ong

14
app.get('/hasToBeAdmin', (req, res, next) => {
  hasRole(req, res, next, 'admin');
}, (req,res) => { 
    // regular route 
});

const hasRole = (req, res, next, role) => {
   if(role != user.role){
      res.redirect('/NotInRole');
   }
   next();
};

ความคิดที่ดีและเรียบง่าย มิดเดิลแวร์จริงๆเป็นเพียงฟังก์ชั่นปกติ ทำไมไม่ส่งผ่านค่าอื่น ๆ ไป โปรดรวม req, res และถัดไปในฟังก์ชันแรก
zevero

1
แม้ว่าโค้ดมักจะพูดเพื่อตัวมันเอง แต่คุณควรเพิ่มคำอธิบายลงในโค้ดของคุณ สิ่งนี้ปรากฏขึ้นในคิวการตรวจสอบเนื่องจากคำตอบแบบใช้รหัสเท่านั้นมักจะเกิดขึ้น
จะ

ฉันคิดถึงวิธีการนั้นก่อนที่จะไป SO แต่ฉันคิดว่า "ฉันมีวิธีที่ดีกว่านี้แน่นอน" หลังจากเยี่ยมชมแล้วฉันเห็นว่านี่เป็นวิธีที่ง่ายและมีประสิทธิภาพมากที่สุด
Fusseldieb

4

หรือหากคุณไม่มีกรณีมากเกินไปหรือถ้าบทบาทไม่ใช่สตริง:

function HasRole(role) {
  return function (req, res, next) {
    if (role !== req.user.role) res.redirect(/* ... */);
    else next();
  }
}

var middlware_hasRoleAdmin = HasRole('admin'); // define router only once

app.get('/hasToBeAdmin', middlware_hasRoleAdmin, function (req, res) {

})

โซลูชันที่หรูหรา
Leos Literak

2

หากคุณมีระดับการอนุญาตหลายระดับคุณสามารถจัดโครงสร้างได้ดังนี้:

const LEVELS = Object.freeze({
  basic: 1,
  pro: 2,
  admin: 3
});

/**
 *  Check if user has the required permission level
 */
module.exports = (role) => {
  return (req, res, next) => {
    if (LEVELS[req.user.role] < LEVELS[role]) return res.status(401).end();
    return next();
  }
}

0

ฉันใช้วิธีนี้ ฉันได้รับโทเค็น jwt ในความต้องการของร่างกายและรับข้อมูลบทบาทจากที่นั่น

//roleMiddleware.js

const checkRole = role => {
    
    return (req, res, next) => {
        if (req.role == role) {
            console.log(`${role} role granted`)
            next()
        } else {
            res.status(401).send({ result: 'error', message: `No ${role} permission granted` })
        }
    }
}

module.exports = { checkRole }

ก่อนอื่นฉันใช้มิดเดิลแวร์รับรองความถูกต้องเพื่อทราบว่าเป็นผู้ใช้ที่ถูกต้องหรือไม่จากนั้นมิดเดิลแวร์บทบาทเพื่อทราบว่าผู้ใช้สามารถเข้าถึงเส้นทาง API ได้หรือไม่

// router.js

router.post('/v1/something-protected', requireAuth, checkRole('commercial'), (req, res) => {
    // do what you want...
})

ผมหวังว่าจะเป็นประโยชน์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.