เหตุผลประการหนึ่งที่ทำให้ ASP.NET ประสบความสำเร็จก็คือ ลดอุปสรรคในการเข้าสู่นักพัฒนาเว็บ คุณไม่จำเป็นต้องจบปริญญาเอกสาขาวิทยาการคอมพิวเตอร์จึงจะเขียนโค้ด ASP.NET ได้ นักพัฒนา ASP.NET จำนวนมากที่ฉันพบในที่ทำงานเรียนรู้ด้วยตนเองและเคยเขียนสเปรดชีต Microsoft® Excel® ก่อนที่จะเขียน C# หรือ Visual Basic® ตอนนี้พวกเขากำลังเขียนเว็บแอปพลิเคชัน และโดยรวมแล้ว พวกเขาสมควรได้รับคำชมสำหรับงานที่พวกเขากำลังทำอยู่
แต่อำนาจมาพร้อมกับความรับผิดชอบ และแม้แต่นักพัฒนา ASP.NET ที่มีประสบการณ์ก็สามารถทำผิดพลาดได้ จากการให้คำปรึกษาเกี่ยวกับโครงการ ASP.NET เป็นเวลาหลายปี ฉันพบว่าข้อผิดพลาดบางอย่างมีแนวโน้มที่จะนำไปสู่ข้อบกพร่องที่เกิดซ้ำโดยเฉพาะ ข้อผิดพลาดบางประการเหล่านี้อาจส่งผลต่อประสิทธิภาพการทำงาน ข้อผิดพลาดอื่นๆ สามารถขัดขวางความสามารถในการขยายขนาดได้ ข้อบกพร่องบางอย่างอาจทำให้ทีมพัฒนาต้องเสียเวลาอันมีค่าในการติดตามข้อบกพร่องและพฤติกรรมที่ไม่คาดคิด
ต่อไปนี้เป็นข้อผิดพลาด 10 ประการที่อาจทำให้เกิดปัญหาในระหว่างการเผยแพร่แอปพลิเคชันการผลิต ASP.NET และวิธีหลีกเลี่ยง ตัวอย่างทั้งหมดมาจากประสบการณ์ของฉันเองในการสร้างแอปพลิเคชันเว็บจริงในบริษัทจริง และในบางกรณี ฉันให้บริบทโดยการอธิบายปัญหาบางอย่างที่ทีมพัฒนา ASP.NET พบในระหว่างกระบวนการพัฒนา
LoadControl และแคชเอาท์พุต มีแอปพลิเคชัน ASP.NET น้อยมากที่ไม่ได้ใช้การควบคุมของผู้ใช้ ก่อนหน้าต้นแบบ นักพัฒนาใช้การควบคุมผู้ใช้เพื่อแยกเนื้อหาทั่วไป เช่น หัวกระดาษและท้ายกระดาษ แม้แต่ใน ASP.NET 2.0 การควบคุมผู้ใช้ยังมอบวิธีที่มีประสิทธิภาพในการสรุปเนื้อหาและพฤติกรรม และแบ่งเพจออกเป็นขอบเขตซึ่งสามารถควบคุมความสามารถในการแคชได้อย่างอิสระจากเพจโดยรวม (กระบวนการที่เรียกว่าเซ็กเมนต์) รูปแบบพิเศษของการแคชเอาต์พุต ).
การควบคุมผู้ใช้สามารถโหลดได้ทั้งแบบเปิดเผยหรือแบบบังคับ การโหลดแบบบังคับอาศัย Page.LoadControl ซึ่งสร้างอินสแตนซ์การควบคุมของผู้ใช้และส่งกลับการอ้างอิงการควบคุม หากการควบคุมผู้ใช้มีสมาชิกของประเภทแบบกำหนดเอง (เช่น ทรัพย์สินสาธารณะ) คุณสามารถส่งการอ้างอิงและเข้าถึงสมาชิกแบบกำหนดเองได้จากโค้ดของคุณ การควบคุมผู้ใช้ในรูปที่ 1 ใช้คุณสมบัติชื่อ BackColor โค้ดต่อไปนี้โหลดการควบคุมผู้ใช้และกำหนดค่าให้กับ BackColor:
protected void Page_Load(object sender, EventArgs e){// โหลดการควบคุมผู้ใช้และเพิ่มลงในเพจ Control control = LoadControl("~/MyUserControl.ascx") ;PlaceHolder1 .Controls.Add(control);//ตั้งค่าสีพื้นหลัง ((MyUserControl)control).BackColor = Color.Yellow;}
จริงๆ แล้วโค้ดข้างต้นนั้นเรียบง่ายมาก แต่มันเป็นกับดักที่รอให้ Developer ที่ไม่ระวังหลงเข้ามา คุณสามารถหาข้อบกพร่องได้หรือไม่?
หากคุณเดาว่าปัญหาเกี่ยวข้องกับแคชเอาต์พุต แสดงว่าคุณถูกต้อง อย่างที่คุณเห็น ตัวอย่างโค้ดข้างต้นคอมไพล์และทำงานได้ดี แต่ถ้าคุณพยายามเพิ่มคำสั่งต่อไปนี้ (ซึ่งถูกต้องตามกฎหมายอย่างสมบูรณ์) ลงใน MyUserControl.ascx:
<%@ OutputCache Duration="5" VaryByParam="None" %>
จากนั้นในครั้งถัดไปที่คุณเรียกใช้เพจ คุณจะเห็น InvalidCastException (โอ้ จอย!) และข้อความแสดงข้อผิดพลาดต่อไปนี้:
"ไม่สามารถส่งออบเจ็กต์ประเภท 'System.Web.UI.PartialCachingControl' เพื่อพิมพ์ 'MyUserControl'"
ดังนั้นโค้ดนี้จึงทำงานได้ดีโดยไม่มีคำสั่ง OutputCache แต่จะล้มเหลวหากมีการเพิ่มคำสั่ง OutputCache ASP.NET ไม่ควรทำงานในลักษณะนี้ หน้า (และการควบคุม) ควรไม่เชื่อเรื่องแคชเอาท์พุต แล้วนี่หมายความว่าอะไร?
ปัญหาคือว่าเมื่อมีการเปิดใช้งานการแคชผลลัพธ์สำหรับการควบคุมผู้ใช้ LoadControl จะไม่ส่งกลับการอ้างอิงไปยังอินสแตนซ์การควบคุมอีกต่อไป แต่จะส่งคืนการอ้างอิงไปยังอินสแตนซ์ PartialCachingControl ซึ่งอาจหรืออาจจะไม่ตัดอินสแตนซ์การควบคุม ขึ้นอยู่กับว่า เอาต์พุตของการควบคุมคือแคช ดังนั้น ถ้านักพัฒนาเรียก LoadControl เพื่อโหลดตัวควบคุมผู้ใช้แบบไดนามิก และแปลงการอ้างอิงตัวควบคุมเพื่อเข้าถึงวิธีการและคุณสมบัติเฉพาะของตัวควบคุม พวกเขาจะต้องใส่ใจกับวิธีการที่พวกเขาทำเช่นนี้เพื่อให้โค้ดจะทำงานโดยไม่คำนึงว่ามีการ คำสั่ง OutputCache
รูปที่ 2 แสดงวิธีที่ถูกต้องในการโหลดการควบคุมของผู้ใช้แบบไดนามิก และแปลงการอ้างอิงการควบคุมที่ส่งคืน ต่อไปนี้เป็นบทสรุปเกี่ยวกับวิธีการทำงาน:
• หากไฟล์ ASCX ไม่มีคำสั่ง OutputCache LoadControl จะส่งกลับการอ้างอิง MyUserControl Page_Load แปลงการอ้างอิงเป็น MyUserControl และตั้งค่าคุณสมบัติ BackColor ของตัวควบคุม
• หากไฟล์ ASCX มีคำสั่ง OutputCache และเอาต์พุตของตัวควบคุมไม่ได้ถูกแคช LoadControl จะส่งกลับการอ้างอิงไปยัง PartialCachingControl ซึ่งคุณสมบัติ CachedControl มีการอ้างอิงไปยัง MyUserControl พื้นฐาน Page_Load แปลง PartialCachingControl.CachedControl เป็น MyUserControl และตั้งค่าคุณสมบัติ BackColor ของตัวควบคุม
• หากไฟล์ ASCX มีคำสั่ง OutputCache และเอาต์พุตของตัวควบคุมถูกแคชไว้ LoadControl จะส่งกลับการอ้างอิงไปยัง PartialCachingControl ซึ่งคุณสมบัติ CachedControl ว่างเปล่า โปรดทราบว่า Page_Load จะไม่ดำเนินการอีกต่อไป ไม่สามารถตั้งค่าคุณสมบัติ BackColor ของตัวควบคุมได้เนื่องจากผลลัพธ์ของตัวควบคุมมาจากแคชผลลัพธ์ กล่าวอีกนัยหนึ่งคือไม่มี MyUserControl ที่จะตั้งค่าคุณสมบัติเลย
รหัสในรูปที่ 2 จะทำงานโดยไม่คำนึงว่ามีคำสั่ง OutputCache ในไฟล์ .ascx หรือไม่ แม้ว่าจะดูซับซ้อนกว่าเล็กน้อย แต่ก็สามารถหลีกเลี่ยงข้อผิดพลาดที่น่ารำคาญได้ ความเรียบง่ายไม่ได้หมายความว่าจะดูแลรักษาง่ายเสมอไป
กลับไปที่เซสชันบนสุดและการแคชเอาท์พุต เมื่อพูดถึงเอาท์พุตแคช ทั้ง ASP.NET 1.1 และ ASP.NET 2.0 มีปัญหาที่อาจเกิดขึ้นซึ่งส่งผลต่อเพจแคชเอาท์พุตในเซิร์ฟเวอร์ที่ใช้ Windows Server™ 2003 และ IIS 6.0 โดยส่วนตัวแล้วฉันเห็นว่าปัญหานี้เกิดขึ้นสองครั้งในเซิร์ฟเวอร์การผลิต ASP.NET และทั้งสองครั้งได้รับการแก้ไขด้วยการปิดการบัฟเฟอร์เอาต์พุต ต่อมาฉันได้เรียนรู้ว่ามีวิธีแก้ปัญหาที่ดีกว่าการปิดใช้งานการแคชเอาต์พุต นี่คือลักษณะที่ปรากฏเมื่อฉันพบปัญหานี้ครั้งแรก
สิ่งที่เกิดขึ้นคือเว็บไซต์ (ขอเรียกว่า Contoso.com ที่นี่ ซึ่งเรียกใช้แอปพลิเคชันอีคอมเมิร์ซสาธารณะในขอบเขตเว็บ ASP.NET ขนาดเล็ก) ติดต่อทีมของฉันโดยบ่นว่าพวกเขากำลังประสบกับข้อผิดพลาดแบบ "ข้ามเธรด" ลูกค้าที่ใช้เว็บไซต์ Contoso.com มักจะสูญเสียข้อมูลที่ป้อนไปอย่างกะทันหัน แต่กลับเห็นข้อมูลที่เกี่ยวข้องกับผู้ใช้รายอื่นแทน หลังจากการวิเคราะห์เล็กน้อย เราพบว่าคำอธิบายของ cross-threading ไม่ถูกต้อง ข้อผิดพลาด "cross-session" มีความเหมาะสมมากกว่า ดูเหมือนว่า Contoso.com จัดเก็บข้อมูลในสถานะเซสชัน และด้วยเหตุผลบางประการ ผู้ใช้จึงเชื่อมต่อกับเซสชันของผู้ใช้รายอื่นเป็นครั้งคราวและแบบสุ่ม
สมาชิกในทีมของฉันคนหนึ่งเขียนเครื่องมือวินิจฉัยที่บันทึกองค์ประกอบสำคัญของคำขอและการตอบกลับ HTTP แต่ละรายการ รวมถึงส่วนหัวของคุกกี้ จากนั้นเขาก็ติดตั้งเครื่องมือบนเว็บเซิร์ฟเวอร์ของ Contoso.com และปล่อยให้เครื่องมือทำงานสองสามวัน ผลลัพธ์ที่ได้ชัดเจนมาก ประมาณหนึ่งครั้งในทุกๆ 100,000 คำขอ ASP.NET จะกำหนด ID เซสชันให้กับเซสชันใหม่อย่างถูกต้องและส่งกลับ ID เซสชันในส่วนหัว Set-Cookie จากนั้นจะส่งกลับรหัสเซสชันเดียวกัน (นั่นคือส่วนหัว Set-Cookie เดียวกัน) ในคำขอถัดไปที่อยู่ติดกัน แม้ว่าคำขอจะเชื่อมโยงกับเซสชันที่ถูกต้องอยู่แล้วและรหัสเซสชันในคุกกี้ถูกส่งอย่างถูกต้อง ส่งผลให้ ASP.NET สุ่มสลับผู้ใช้ออกจากเซสชันของตนเองและเชื่อมต่อกับเซสชันอื่น
เราประหลาดใจและออกเดินทางเพื่อหาสาเหตุ ก่อนอื่น เราตรวจสอบซอร์สโค้ดของ Contoso.com และเพื่อเป็นการบรรเทาทุกข์ ปัญหาไม่ได้อยู่ที่นั่น ต่อไป เพื่อให้แน่ใจว่าปัญหาไม่เกี่ยวข้องกับโฮสต์แอปพลิเคชันในขอบเขตเว็บ เราเหลือเพียงเซิร์ฟเวอร์เดียวที่ทำงานและปิดเซิร์ฟเวอร์ที่เหลือทั้งหมด ปัญหายังคงมีอยู่ ซึ่งไม่ใช่เรื่องน่าแปลกใจเนื่องจากบันทึกของเราแสดงให้เห็นว่าส่วนหัว Set-Cookie ที่ตรงกันไม่เคยมาจากเซิร์ฟเวอร์สองแห่งที่แตกต่างกัน ASP.NET สร้าง ID เซสชันที่ซ้ำกันโดยไม่ได้ตั้งใจ ซึ่งน่าทึ่งมากเนื่องจากใช้คลาส .NET Framework RNGCryptoServiceProvider เพื่อสร้าง ID เหล่านี้ และ ID เซสชันนั้นยาวพอที่จะให้แน่ใจว่า ID เดียวกันจะไม่ถูกสร้างสองครั้ง (อย่างน้อยก็ในครั้งต่อไป จะไม่เกิดขึ้นสองครั้งในล้านล้านปี) นอกเหนือจากนั้น แม้ว่า RNGCryptoServiceProvider จะสร้างตัวเลขสุ่มซ้ำๆ โดยไม่ตั้งใจ แต่ก็ไม่ได้อธิบายว่าทำไม ASP.NET จึงแทนที่ ID เซสชันที่ถูกต้องด้วยรหัสใหม่อย่างลึกลับ (ซึ่งไม่ซ้ำกัน)
โดยสรุปแล้ว เราจึงตัดสินใจดูการแคชเอาต์พุต เมื่อ OutputCacheModule แคชการตอบสนอง HTTP จะต้องระวังอย่าแคชส่วนหัว Set-Cookie มิฉะนั้น การตอบสนองที่แคชไว้ซึ่งมี ID เซสชันใหม่จะเชื่อมต่อผู้รับทั้งหมดของการตอบสนองที่แคชไว้ (และผู้ใช้ที่คำขอสร้างการตอบสนองที่แคชไว้) ไปยังเซสชันเดียวกัน เราตรวจสอบซอร์สโค้ดแล้ว Contoso.com ได้เปิดใช้งานการแคชเอาต์พุตในทั้งสองเพจ เราปิดการแคชเอาต์พุต ด้วยเหตุนี้ แอปพลิเคชันจึงทำงานเป็นเวลาหลายวันโดยไม่มีปัญหาข้ามเซสชันแม้แต่ครั้งเดียว หลังจากนั้นก็ดำเนินไปโดยไม่มีข้อผิดพลาดใดๆ เป็นเวลากว่าสองปี ในบริษัทอื่นที่มีแอปพลิเคชันที่แตกต่างกันและชุดเว็บเซิร์ฟเวอร์ที่แตกต่างกัน เราเห็นปัญหาเดียวกันนี้หายไปเลย เช่นเดียวกับที่ Contoso.com การกำจัดแคชเอาต์พุตจะช่วยแก้ปัญหาได้
Microsoft ยืนยันในภายหลังว่าลักษณะการทำงานนี้เกิดจากปัญหาใน OutputCacheModule (การอัปเดตอาจได้รับการเผยแพร่ตามเวลาที่คุณอ่านบทความนี้) เมื่อใช้ ASP.NET กับ IIS 6.0 และแคชโหมดเคอร์เนลถูกเปิดใช้งาน บางครั้ง OutputCacheModule ก็ล้มเหลวในการลบส่วนหัว Set-Cookie จากการตอบสนองแคชที่ส่งผ่าน ไปที่ Http.sys ต่อไปนี้เป็นลำดับเฉพาะของเหตุการณ์ที่ส่งผลให้เกิดข้อผิดพลาด:
• ผู้ใช้ที่ไม่ได้เยี่ยมชมไซต์เมื่อเร็ว ๆ นี้ (และดังนั้นจึงไม่มีเซสชันที่เกี่ยวข้อง) ร้องขอเพจที่เปิดใช้งานแคชเอาต์พุต แต่เอาต์พุตไม่พร้อมใช้งานในปัจจุบัน ในแคช
• คำขอดำเนินการโค้ดที่เข้าถึงเซสชันที่สร้างขึ้นล่าสุดของผู้ใช้ ทำให้คุกกี้ ID เซสชันถูกส่งคืนในส่วนหัว Set-Cookie ของการตอบกลับ
• OutputCacheModule จัดเตรียมเอาต์พุตให้กับ Http.sys แต่ไม่สามารถลบส่วนหัว Set-Cookie ออกจากการตอบสนองได้
• Http.sys ส่งคืนการตอบสนองที่แคชไว้ตามคำขอครั้งต่อ ๆ ไป ทำให้เกิดการเชื่อมต่อผู้ใช้รายอื่นเข้ากับเซสชันโดยไม่ตั้งใจ
คุณธรรมของเรื่องราวคืออะไร สถานะเซสชันและแคชเอาต์พุตโหมดเคอร์เนลไม่ปะปนกัน ถ้าคุณใช้สถานะเซสชันในเพจที่เปิดใช้งานแคชเอาต์พุต และแอปพลิเคชันกำลังทำงานบน IIS 6.0 คุณจำเป็นต้องปิดแคชเอาต์พุตโหมดเคอร์เนล คุณจะยังคงได้รับประโยชน์จากการแคชเอาต์พุต แต่เนื่องจากการแคชเอาต์พุตในโหมดเคอร์เนลเร็วกว่าการแคชเอาต์พุตปกติมาก การแคชจึงไม่มีประสิทธิภาพเท่าที่ควร สำหรับข้อมูลเพิ่มเติมเกี่ยวกับปัญหานี้ โปรดดู support.microsoft.com/kb/917072
คุณสามารถปิดแคชเอาต์พุตโหมดเคอร์เนลสำหรับแต่ละเพจได้โดยรวมแอตทริบิวต์ VaryByParam="*" ในคำสั่ง OutputCache ของเพจ แม้ว่าการทำเช่นนี้อาจส่งผลให้ความต้องการหน่วยความจำเพิ่มขึ้นอย่างกะทันหัน อีกวิธีที่ปลอดภัยกว่าคือการปิดการแคชโหมดเคอร์เนลสำหรับแอปพลิเคชันทั้งหมดโดยรวมองค์ประกอบต่อไปนี้ใน web.config:
<httpRuntime EnableKernelOutputCache="false" />
คุณยังสามารถใช้การตั้งค่ารีจิสทรีเพื่อปิดใช้งานแคชเอาต์พุตโหมดเคอร์เนลได้ทั่วโลก นั่นคือ ปิดใช้งานแคชเอาต์พุตโหมดเคอร์เนลสำหรับเซิร์ฟเวอร์ทั้งหมด ดูsupport.microsoft.com/kb/820129สำหรับรายละเอียด
ทุกครั้งที่ฉันได้ยินลูกค้ารายงานปัญหาเซสชันที่น่าสงสัย ฉันจะถามพวกเขาว่าพวกเขากำลังใช้การแคชเอาต์พุตบนเพจใดๆ หรือไม่ หากพวกเขาใช้การแคชเอาต์พุตและระบบปฏิบัติการโฮสต์คือ Windows Server 2003 ฉันขอแนะนำให้พวกเขาปิดใช้งานการแคชเอาต์พุตโหมดเคอร์เนล ปัญหามักจะได้รับการแก้ไข หากปัญหาไม่ได้รับการแก้ไข แสดงว่ามีจุดบกพร่องอยู่ในโค้ด ระวัง
!
อายุการใช้งาน Ticket Authentication Forms คุณสามารถระบุปัญหาด้วยรหัสต่อไปนี้ได้หรือไม่?
FormsAuthentication.RedirectFromLoginPage (ชื่อผู้ใช้ จริง);
รหัสนี้อาจดูเหมือนใช้ได้ แต่ไม่ควรใช้ในแอปพลิเคชัน ASP.NET 1.x เว้นแต่รหัสที่อื่นในแอปพลิเคชันจะชดเชยผลกระทบด้านลบของคำสั่งนี้ หากคุณไม่แน่ใจว่าทำไม โปรดอ่านต่อ
FormsAuthentication.RedirectFromLoginPage ดำเนินการสองงาน ขั้นแรก เมื่อ FormsAuthenticationModule เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าล็อกอิน FormsAuthentication.RedirectFromLoginPage เปลี่ยนเส้นทางผู้ใช้ไปยังเพจที่พวกเขาร้องขอในตอนแรก ประการที่สอง จะออกตั๋วการรับรองความถูกต้อง (โดยปกติจะอยู่ในคุกกี้ และจะอยู่ในคุกกี้ใน ASP.NET 1.x เสมอ) ซึ่งอนุญาตให้ผู้ใช้ยังคงได้รับการรับรองความถูกต้องตามระยะเวลาที่กำหนดไว้ล่วงหน้า
ปัญหาอยู่ในช่วงเวลานี้ ใน ASP.NET 1.x การส่งผ่านพารามิเตอร์อื่นที่เป็นเท็จไปยัง RedirectFromLoginPage จะออกตั๋วการรับรองความถูกต้องชั่วคราวที่หมดอายุหลังจาก 30 นาทีตามค่าเริ่มต้น (คุณสามารถเปลี่ยนระยะเวลาการหมดเวลาได้โดยใช้แอตทริบิวต์การหมดเวลาในองค์ประกอบของ web.config) อย่างไรก็ตาม การส่งพารามิเตอร์อื่นเป็น true จะออกตั๋วการตรวจสอบสิทธิ์แบบถาวรซึ่งมีอายุ 50 ปี ซึ่งจะสร้างปัญหาได้เนื่องจากหากมีคนขโมยการรับรองความถูกต้องนั้น ตั๋ว พวกเขาสามารถใช้ข้อมูลประจำตัวของเหยื่อเพื่อเข้าถึงเว็บไซต์ตลอดระยะเวลาของตั๋ว มีหลายวิธีในการขโมยตั๋วการรับรองความถูกต้อง — ตรวจสอบการรับส่งข้อมูลที่ไม่ได้เข้ารหัสบนจุดเชื่อมต่อไร้สายสาธารณะ, การเขียนสคริปต์ข้ามเว็บไซต์, เข้าถึงคอมพิวเตอร์ของเหยื่อ ฯลฯ — ดังนั้นการส่ง True ไปยัง RedirectFromLoginPage จึงปลอดภัยกว่าการปิดการใช้งานเว็บไซต์ของคุณ ไม่ได้ดีไปกว่านี้มากนัก โชคดีที่ปัญหานี้ได้รับการแก้ไขแล้วใน ASP.NET 2.0 ตอนนี้ RedirectFromLoginPage ยอมรับการหมดเวลาที่ระบุใน web.config สำหรับตั๋วการตรวจสอบสิทธิ์ชั่วคราวและถาวรในลักษณะเดียวกัน
วิธีแก้ปัญหาหนึ่งคือไม่ต้องส่งค่าจริงในพารามิเตอร์ตัวที่สองของ RedirectFromLoginPage ในแอปพลิเคชัน ASP.NET 1.x แต่วิธีนี้ทำไม่ได้เนื่องจากหน้าเข้าสู่ระบบมักจะมีช่อง "ให้ฉันอยู่ในระบบต่อไป" ซึ่งผู้ใช้สามารถเลือกเพื่อรับคุกกี้การตรวจสอบสิทธิ์แบบถาวร แทนที่จะเป็นคุกกี้การตรวจสอบสิทธิ์ชั่วคราว อีกวิธีหนึ่งคือการใช้ข้อมูลโค้ดใน Global.asax (หรือโมดูล HTTP หากคุณต้องการ) ซึ่งจะแก้ไขคุกกี้ที่มีตั๋วการตรวจสอบสิทธิ์ถาวรก่อนที่จะส่งคืนไปยังเบราว์เซอร์
รูปที่ 3 มีข้อมูลโค้ดดังกล่าวหนึ่งรายการ หากข้อมูลโค้ดนี้อยู่ใน Global.asax จะมีการปรับเปลี่ยนคุณสมบัติ Expires ของคุกกี้การรับรองความถูกต้องของแบบฟอร์มถาวรขาออก เพื่อให้คุกกี้หมดอายุหลังจาก 24 ชั่วโมง คุณสามารถตั้งค่าการหมดเวลาเป็นวันที่ใดก็ได้ที่คุณต้องการโดยแก้ไขบรรทัดที่แสดงความคิดเห็นว่า "วันหมดอายุใหม่"
คุณอาจพบว่ามันแปลกที่เมธอด Application_EndRequest เรียกเมธอด Helper ในเครื่อง (GetCookieFromResponse) เพื่อตรวจสอบคุกกี้การรับรองความถูกต้องสำหรับการตอบกลับขาออก วิธีการตัวช่วยเหลือเป็นวิธีแก้ปัญหาสำหรับจุดบกพร่องอื่นใน ASP.NET 1.1 ที่ทำให้คุกกี้ปลอมถูกเพิ่มลงในการตอบสนองหากคุณใช้ตัวสร้างดัชนีสตริงของ HttpCookieCollection เพื่อตรวจสอบคุกกี้ที่ไม่มีอยู่จริง การใช้ตัวสร้างดัชนีจำนวนเต็มเนื่องจาก GetCookieFromResponse ช่วยแก้ปัญหาได้
กลับสู่สถานะมุมมองด้านบน: นักฆ่าประสิทธิภาพเงียบ ในแง่หนึ่ง สถานะมุมมองคือสิ่งที่ยิ่งใหญ่ที่สุดเท่าที่เคยมีมา ท้ายที่สุดแล้ว สถานะการดูจะทำให้เพจและส่วนควบคุมสามารถรักษาสถานะระหว่าง postback ได้ ดังนั้น คุณจึงไม่จำเป็นต้องเขียนโค้ดเพื่อป้องกันไม่ให้ข้อความในกล่องข้อความหายไปเมื่อมีการคลิกปุ่ม หรือเพื่อสอบถามฐานข้อมูลใหม่และเชื่อมโยง DataGrid ใหม่หลัง postback เช่นเดียวกับที่คุณทำใน ASP แบบดั้งเดิม
แต่สถานะการดูมีข้อเสีย: เมื่อมันใหญ่เกินไป มันจะกลายเป็นนักฆ่าที่เงียบงัน ตัวควบคุมบางอย่าง เช่น กล่องข้อความ จะทำการตัดสินใจตามสถานะมุมมอง การควบคุมอื่นๆ (โดยเฉพาะ DataGrid และ GridView) จะกำหนดสถานะมุมมองตามจำนวนข้อมูลที่แสดง ฉันจะกลัวถ้า GridView แสดงข้อมูล 200 หรือ 300 แถว แม้ว่าสถานะมุมมอง ASP.NET 2.0 มีขนาดประมาณครึ่งหนึ่งของสถานะมุมมอง ASP.NET 1.x แต่ GridView ที่ไม่ถูกต้องสามารถลดแบนด์วิธที่มีประสิทธิภาพของการเชื่อมต่อระหว่างเบราว์เซอร์และเว็บเซิร์ฟเวอร์ได้ 50% หรือมากกว่านั้นได้อย่างง่ายดาย
คุณสามารถปิดสถานะมุมมองสำหรับการควบคุมแต่ละรายการได้โดยการตั้งค่า EnableViewState เป็นเท็จ แต่การควบคุมบางอย่าง (โดยเฉพาะ DataGrid) จะสูญเสียฟังก์ชันการทำงานบางอย่างเมื่อไม่สามารถใช้สถานะมุมมองได้ ทางออกที่ดีกว่าสำหรับการควบคุมสถานะมุมมองคือเก็บไว้บนเซิร์ฟเวอร์ ใน ASP.NET 1.x คุณสามารถแทนที่วิธี LoadPageStateFromPersistenceMedium และ SavePageStateToPersistenceMedium ของเพจได้ และจัดการสถานะมุมมองตามที่คุณต้องการ รหัสในรูปที่ 4 แสดงการแทนที่ที่ป้องกันไม่ให้สถานะมุมมองถูกเก็บไว้ในฟิลด์ที่ซ่อนอยู่และเก็บไว้ในสถานะเซสชันแทน การจัดเก็บสถานะมุมมองในสถานะเซสชันจะมีผลโดยเฉพาะอย่างยิ่งเมื่อใช้กับแบบจำลองกระบวนการสถานะเซสชันเริ่มต้น (นั่นคือ เมื่อสถานะเซสชันถูกเก็บไว้ในกระบวนการของผู้ปฏิบัติงาน ASP.NET ในหน่วยความจำ) ในทางตรงกันข้าม หากสถานะเซสชันถูกจัดเก็บไว้ในฐานข้อมูล มีเพียงการทดสอบเท่านั้นที่สามารถแสดงว่าการรักษาสถานะมุมมองในสถานะเซสชันช่วยปรับปรุงหรือลดประสิทธิภาพได้หรือไม่
วิธีการเดียวกันนี้ใช้ใน ASP.NET 2.0 แต่ ASP.NET 2.0 ให้วิธีที่ง่ายกว่าในการคงสถานะการดูในสถานะเซสชัน ขั้นแรก ให้กำหนดอะแดปเตอร์เพจแบบกำหนดเองซึ่งมีเมธอด GetStatePersister ส่งคืนอินสแตนซ์ของคลาส .NET Framework SessionPageStatePersister:
คลาสสาธารณะ SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public แทนที่ PageStatePersister GetStatePersister () {return new SessionPageStatePersister(this.Page ) ; }}
จากนั้น ลงทะเบียนอะแดปเตอร์เพจแบบกำหนดเองเป็นอะแดปเตอร์เพจเริ่มต้นโดยการวางไฟล์ App.browsers ลงในโฟลเดอร์ App_Browsers ของแอปพลิเคชันของคุณดังต่อไปนี้:
<browsers><browser refID="Default"><controlAdapters><adapter controlType=" System.Web UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>
(คุณสามารถตั้งชื่อไฟล์อะไรก็ได้ที่คุณต้องการ ตราบใดที่ไฟล์นั้นมีนามสกุล .browsers) หลังจากนั้น ASP.NET จะโหลดอะแดปเตอร์เพจ และใช้ SessionPageStatePersister ที่ส่งคืนเพื่อรักษาสถานะของเพจทั้งหมด รวมถึงสถานะการดูด้วย
ข้อเสียประการหนึ่งของการใช้อะแดปเตอร์เพจแบบกำหนดเองคือ จะใช้แบบโกลบอลกับทุกเพจในแอปพลิเคชัน หากคุณต้องการให้สถานะการดูของบางเพจอยู่ในสถานะเซสชัน แต่ไม่ใช่สถานะอื่น ให้ใช้วิธีการที่แสดงในรูปที่ 4 นอกจากนี้ คุณอาจประสบปัญหาในการใช้วิธีนี้หากผู้ใช้สร้างหน้าต่างเบราว์เซอร์หลายหน้าต่างในเซสชันเดียวกัน
กลับไปด้านบน
สถานะเซสชันของ SQL Server: ตัวทำลายประสิทธิภาพอื่น
ASP.NET ช่วยให้จัดเก็บสถานะเซสชันในฐานข้อมูลได้ง่าย เพียงสลับสวิตช์ใน web.config จากนั้นสถานะเซสชันก็จะถูกย้ายไปยังฐานข้อมูลส่วนหลังได้อย่างง่ายดาย นี่เป็นคุณลักษณะที่สำคัญสำหรับแอปพลิเคชันที่ทำงานในขอบเขตของเว็บ เนื่องจากอนุญาตให้ทุกเซิร์ฟเวอร์ในขอบเขตสามารถแชร์ที่เก็บข้อมูลทั่วไปของสถานะเซสชันได้ กิจกรรมฐานข้อมูลที่เพิ่มเข้ามาจะลดประสิทธิภาพของคำขอแต่ละรายการ แต่ความสามารถในการขยายขนาดที่เพิ่มขึ้นชดเชยการสูญเสียประสิทธิภาพ
ทั้งหมดนี้ฟังดูดี แต่สิ่งต่างๆ เปลี่ยนไปเมื่อคุณพิจารณาประเด็นบางประการ:
• แม้แต่ในแอปพลิเคชันที่ใช้สถานะเซสชัน หน้าเพจส่วนใหญ่ก็ไม่ใช้สถานะเซสชัน
ตามค่าเริ่มต้น ตัวจัดการสถานะเซสชันของ ASP.NET ทำการเข้าถึงสองครั้ง (การเข้าถึงแบบอ่านหนึ่งครั้งและการเข้าถึงการเขียนหนึ่งครั้ง) ไปยังที่เก็บข้อมูลเซสชันในแต่ละคำขอ โดยไม่คำนึงว่าเพจที่ร้องขอจะใช้สถานะเซสชันหรือไม่
กล่าวอีกนัยหนึ่ง เมื่อคุณใช้ตัวเลือกสถานะเซสชันของ SQL Server™ คุณจะต้องจ่ายราคา (การเข้าถึงฐานข้อมูลสองครั้ง) สำหรับทุกคำขอ แม้แต่คำขอสำหรับเพจที่ไม่เกี่ยวข้องกับสถานะเซสชันก็ตาม สิ่งนี้มีผลกระทบเชิงลบโดยตรงต่อปริมาณงานของเว็บไซต์ทั้งหมด
รูปที่ 5 กำจัดการเข้าถึงฐานข้อมูลสถานะเซสชันที่ไม่จำเป็น
แล้วคุณควรทำอย่างไร? ง่ายมาก: ปิดการใช้งานสถานะเซสชันในเพจที่ไม่ได้ใช้สถานะเซสชัน นี่เป็นความคิดที่ดีเสมอ แต่สำคัญอย่างยิ่งเมื่อเก็บสถานะเซสชันไว้ในฐานข้อมูล รูปที่ 5 แสดงวิธีการปิดการใช้งานสถานะเซสชัน หากเพจไม่ได้ใช้สถานะเซสชันเลย ให้รวม EnableSessionState="false" ไว้ในคำสั่งของเพจ ดังนี้:
<%@ Page EnableSessionState="false" ... %>
คำสั่งนี้ป้องกันไม่ให้ผู้จัดการสถานะเซสชันอ่านและเขียนฐานข้อมูลสถานะเซสชันในทุกคำขอ หากเพจอ่านข้อมูลจากสถานะเซสชันแต่ไม่ได้เขียนข้อมูล (นั่นคือ ไม่ได้แก้ไขเนื้อหาของเซสชันของผู้ใช้) ให้ตั้งค่า EnableSessionState เป็น ReadOnly ดังนี้:
<%@ Page EnableSessionState="ReadOnly" ... %>
สุดท้ายนี้ หากเพจต้องการการเข้าถึงแบบอ่าน/เขียนในสถานะเซสชัน ให้ละเว้นคุณสมบัติ EnableSessionState หรือตั้งค่าเป็นจริง:
<%@ Page EnableSessionState="true" ... %>
ด้วยการควบคุมสถานะเซสชันด้วยวิธีนี้ คุณมั่นใจได้ว่า ASP.NET จะเข้าถึงฐานข้อมูลสถานะเซสชันเมื่อจำเป็นจริงๆ เท่านั้น การกำจัดการเข้าถึงฐานข้อมูลที่ไม่จำเป็นถือเป็นขั้นตอนแรกในการสร้างแอปพลิเคชันที่มีประสิทธิภาพสูง
อย่างไรก็ตาม คุณสมบัติ EnableSessionState เป็นแบบสาธารณะ คุณสมบัตินี้ได้รับการบันทึกไว้ตั้งแต่ ASP.NET 1.0 แต่ฉันยังไม่ค่อยเห็นนักพัฒนาใช้ประโยชน์จากมัน อาจเป็นเพราะไม่สำคัญมากสำหรับโมเดลสถานะเซสชันเริ่มต้นในหน่วยความจำ แต่มันเป็นสิ่งสำคัญสำหรับรุ่น SQL Server
กลับไปด้านบน บทบาท Uncached คำสั่งต่อไปนี้มักจะปรากฏในไฟล์ web.config ของแอปพลิเคชัน ASP.NET 2.0 และในตัวอย่างที่แนะนำตัวจัดการบทบาท ASP.NET 2.0:
<roleManager Enabled="true" />
แต่ดังที่แสดงไว้ข้างต้น ข้อความนี้มีผลกระทบด้านลบต่อประสิทธิภาพอย่างมีนัยสำคัญ คุณรู้ไหมว่าทำไม?
ตามค่าเริ่มต้น ตัวจัดการบทบาทของ ASP.NET 2.0 จะไม่แคชข้อมูลบทบาท แต่จะปรึกษาที่เก็บข้อมูลบทบาทในแต่ละครั้งที่ต้องการเพื่อกำหนดบทบาท (ถ้ามี) ของผู้ใช้ ซึ่งหมายความว่าเมื่อผู้ใช้ได้รับการรับรองความถูกต้อง หน้าใดๆ ก็ตามที่ใช้ประโยชน์จากข้อมูลบทบาท (เช่น หน้าที่ใช้แผนผังเว็บไซต์โดยเปิดใช้งานการตั้งค่าการตัดความปลอดภัย และหน้าที่มีการจำกัดการเข้าถึงโดยใช้คำสั่ง URL ตามบทบาทใน web.config) จะทำให้เกิดบทบาทนี้ ผู้จัดการเพื่อสืบค้นที่เก็บข้อมูลบทบาท หากบทบาทถูกจัดเก็บไว้ในฐานข้อมูล คุณสามารถละทิ้งการเข้าถึงฐานข้อมูลหลายฐานข้อมูลสำหรับแต่ละคำขอได้อย่างง่ายดาย วิธีแก้ไขคือการกำหนดค่าตัวจัดการบทบาทเพื่อแคชข้อมูลบทบาทในคุกกี้:
<roleManager Enabled="true" cacheRolesInCookie="true" />
คุณสามารถใช้แอตทริบิวต์ <roleManager> อื่นๆ เพื่อควบคุมคุณลักษณะของคุกกี้บทบาทได้ ตัวอย่างเช่น ระยะเวลาที่คุกกี้จะยังคงใช้งานได้ (และความถี่ที่ผู้จัดการบทบาทกลับไปยังฐานข้อมูลบทบาท) คุกกี้บทบาทจะได้รับการลงนามและเข้ารหัสตามค่าเริ่มต้น ดังนั้นความเสี่ยงด้านความปลอดภัยจึงลดลงแม้จะไม่เป็นศูนย์ก็ตาม
กลับไปด้านบนการทำให้เป็นอนุกรมคุณสมบัติไฟล์การกำหนดค่า
บริการโปรไฟล์ ASP.NET 2.0 มอบโซลูชันสำเร็จรูปสำหรับปัญหาการรักษาสถานะต่อผู้ใช้ เช่น การกำหนดลักษณะส่วนบุคคลและการกำหนดลักษณะภาษา หากต้องการใช้บริการโปรไฟล์ คุณต้องกำหนดโปรไฟล์ XML ที่มีแอตทริบิวต์ที่คุณต้องการเก็บรักษาไว้ในนามของผู้ใช้แต่ละราย จากนั้น ASP.NET จะคอมไพล์คลาสที่มีคุณสมบัติเดียวกันและให้การเข้าถึงอินสแตนซ์ของคลาสอย่างเข้มงวดผ่านคุณสมบัติไฟล์การกำหนดค่าที่เพิ่มลงในเพจ
ความยืดหยุ่นของโปรไฟล์นั้นยอดเยี่ยมมากจนทำให้สามารถใช้ประเภทข้อมูลที่กำหนดเองเป็นคุณสมบัติของโปรไฟล์ได้ อย่างไรก็ตามมีปัญหาที่ฉันได้เห็นเป็นการส่วนตัวที่ทำให้นักพัฒนาทำผิดพลาด รูปที่ 6 ประกอบด้วยคลาสแบบง่ายชื่อโพสต์ และคำจำกัดความโปรไฟล์ที่ใช้โพสต์เป็นแอตทริบิวต์โปรไฟล์ อย่างไรก็ตาม คลาสนี้และไฟล์การกำหนดค่านี้ก่อให้เกิดการทำงานที่ไม่คาดคิดขณะรันไทม์ คุณช่วยคิดออกได้ไหมว่าทำไม?
ปัญหาคือว่าโพสต์มีช่องส่วนตัวที่เรียกว่า _count ซึ่งจะต้องทำให้เป็นอนุกรมและดีซีเรียลไลซ์เพื่อที่จะหยุดและตรึงอินสแตนซ์ของคลาสอีกครั้ง อย่างไรก็ตาม _count จะไม่ซีเรียลไลซ์และดีซีเรียลไลซ์เนื่องจากเป็นแบบส่วนตัว และตัวจัดการส่วนกำหนดค่า ASP.NET ใช้การทำให้ซีเรียลไลซ์ XML ตามค่าเริ่มต้นเพื่อทำให้ซีเรียลไลซ์และดีซีเรียลไลซ์ชนิดแบบกำหนดเอง ตัวสร้างอนุกรม XML ละเว้นสมาชิกที่ไม่ใช่แบบสาธารณะ ดังนั้น อินสแตนซ์ของโพสต์จะถูกทำให้เป็นอนุกรมและดีซีเรียลไลซ์ แต่ทุกครั้งที่อินสแตนซ์ของคลาสถูกดีซีเรียลไลซ์ _count จะถูกรีเซ็ตเป็น 0
วิธีแก้ปัญหาหนึ่งคือทำให้ _count เป็นฟิลด์สาธารณะแทนที่จะเป็นฟิลด์ส่วนตัว อีกวิธีหนึ่งคือการห่อหุ้ม _count ด้วยคุณสมบัติการอ่าน/เขียนสาธารณะ ทางออกที่ดีที่สุดคือการทำเครื่องหมายโพสต์ว่าเป็นซีเรียลไลซ์ได้ (โดยใช้ SerializableAttribute) และกำหนดค่าตัวจัดการโปรไฟล์ให้ใช้ .NET Framework binary serializer เพื่อทำให้ซีเรียลไลซ์และดีซีเรียลไลซ์คลาสอินสแตนซ์ โซลูชันนี้จะรักษาการออกแบบของคลาสไว้ ต่างจาก XML serializers ตัวซีเรียลไลเซอร์แบบไบนารีจะทำให้ฟิลด์เป็นอนุกรมโดยไม่คำนึงว่าจะสามารถเข้าถึงได้หรือไม่ รูปที่ 7 แสดงคลาส Post เวอร์ชันที่แก้ไขแล้ว และเน้นการเปลี่ยนแปลงคำจำกัดความโปรไฟล์ที่มากับการเปลี่ยนแปลง
สิ่งหนึ่งที่คุณควรจำไว้ก็คือ หากคุณใช้ประเภทข้อมูลที่กำหนดเองเป็นคุณสมบัติโปรไฟล์ และประเภทข้อมูลนั้นมีสมาชิกข้อมูลที่ไม่เปิดเผยต่อสาธารณะซึ่งจะต้องทำให้เป็นอนุกรมเพื่อที่จะทำให้อินสแตนซ์ของประเภทเป็นอนุกรมโดยสมบูรณ์ ให้ใช้ serializeAs=" Binary" ในคุณสมบัติการประกาศคุณสมบัติและตรวจสอบให้แน่ใจว่าประเภทนั้นสามารถซีเรียลไลซ์ได้ มิฉะนั้น การซีเรียลไลซ์แบบเต็มจะไม่เกิดขึ้น และคุณจะเสียเวลาในการพยายามหาสาเหตุที่ทำให้โปรไฟล์ไม่ทำงาน
กลับไปด้านบน ความอิ่มตัวของเธรดพูล ฉันมักจะประหลาดใจมากกับจำนวนเพจ ASP.NET ที่แท้จริงที่ฉันเห็นเมื่อดำเนินการสืบค้นฐานข้อมูล และรอเป็นเวลา 15 วินาทีขึ้นไปเพื่อให้ผลลัพธ์การสืบค้นถูกส่งกลับ (ฉันรอ 15 นาทีก่อนที่จะเห็นผลลัพธ์ของการสืบค้นของฉัน!) บางครั้งความล่าช้าเป็นผลที่หลีกเลี่ยงไม่ได้จากการที่ข้อมูลจำนวนมากถูกส่งคืน หรือในบางครั้งความล่าช้านั้นเกิดจากการออกแบบฐานข้อมูลที่ไม่ดี แต่ไม่คำนึงถึงเหตุผล การสอบถามฐานข้อมูลแบบยาวหรือการดำเนินการ I/O แบบยาวชนิดใดๆ จะทำให้ปริมาณงานลดลงในแอปพลิเคชัน ASP.NET
ฉันได้อธิบายปัญหานี้โดยละเอียดแล้ว ดังนั้นฉันจะไม่ลงรายละเอียดมากเกินไปที่นี่ พอจะกล่าวได้ว่า ASP.NET อาศัยเธรดพูลที่จำกัดในการจัดการคำขอ หากเธรดทั้งหมดถูกครอบครองเพื่อรอการสืบค้นฐานข้อมูล การเรียกบริการเว็บ หรือการดำเนินการ I/O อื่นๆ ให้เสร็จสิ้น เธรดเหล่านั้นจะถูกเผยแพร่เมื่อมีการดำเนินการ เสร็จสมบูรณ์ ก่อนที่จะออกเธรด คำขออื่น ๆ จะต้องเข้าคิวและรอ เมื่อมีการจัดคิวคำขอ ประสิทธิภาพจะลดลงอย่างมาก หากคิวเต็ม ASP.NET จะทำให้การร้องขอในภายหลังล้มเหลวโดยมีข้อผิดพลาด HTTP 503 นี่ไม่ใช่สถานการณ์ที่เราอยากเห็นในแอปพลิเคชันที่ใช้งานจริงบนเว็บเซิร์ฟเวอร์ที่ใช้งานจริง
วิธีแก้ปัญหาคือเพจแบบอะซิงโครนัส ซึ่งเป็นหนึ่งในฟีเจอร์ที่ดีที่สุดแต่ไม่ค่อยมีใครรู้จักของ ASP.NET 2.0 การร้องขอเพจแบบอะซิงโครนัสเริ่มต้นบนเธรด แต่เมื่อเริ่มการดำเนินการ I/O ก็จะส่งกลับไปยังเธรดนั้นและอินเทอร์เฟซ IAsyncResult ของ ASP.NET เมื่อการดำเนินการเสร็จสมบูรณ์ การร้องขอจะแจ้ง ASP.NET ผ่าน IAsyncResult และ ASP.NET จะดึงเธรดอื่นจากพูลและดำเนินการประมวลผลคำขอให้เสร็จสิ้น เป็นที่น่าสังเกตว่าเมื่อการดำเนินการ I/O เกิดขึ้น ไม่มีเธรดพูลเธรดที่ถูกครอบครอง สิ่งนี้สามารถปรับปรุงปริมาณงานได้อย่างมากโดยการป้องกันไม่ให้คำขอสำหรับเพจอื่น (เพจที่ไม่ได้ดำเนินการ I/O ที่มีความยาว) ไม่ให้รออยู่ในคิว
คุณสามารถอ่านทั้งหมดเกี่ยวกับหน้าอะซิงโครนัสได้ในนิตยสาร MSDN® ฉบับเดือนตุลาคม 2548 เพจใดๆ ที่ผูกกับ I/O แทนที่จะผูกกับเครื่องและใช้เวลานานในการดำเนินการ มีโอกาสที่จะกลายเป็นเพจแบบอะซิงโครนัส
เมื่อฉันบอกนักพัฒนาเกี่ยวกับเพจแบบอะซิงโครนัส พวกเขามักจะตอบกลับว่า "เยี่ยมมาก แต่ฉันไม่ต้องการมันในแอปพลิเคชันของฉัน" ซึ่งฉันตอบกลับไปว่า "เพจใดของคุณจำเป็นต้องสืบค้นฐานข้อมูลหรือไม่" คุณได้ตรวจสอบตัวนับประสิทธิภาพของ ASP.NET เพื่อดูสถิติเกี่ยวกับคำขอที่อยู่ในคิวและเวลารอโดยเฉลี่ยแล้วหรือยัง แม้ว่าแอปพลิเคชันของคุณจะทำงานได้ดีเมื่อขนาดไคลเอนต์ของคุณเพิ่มขึ้น แต่ภาระก็อาจเพิ่ม
ขึ้น ของแอปพลิเคชัน ASP.NET ในโลกแห่งความเป็นจริงจำเป็นต้องมีเพจแบบอะซิงโครนัส โปรดจำไว้ว่านี้!
กลับไปด้านบน การแอบอ้างบุคคลอื่นและการอนุญาต ACL ต่อไปนี้เป็นคำสั่งการกำหนดค่าง่ายๆ แต่มันทำให้ตาของฉันสว่างทุกครั้งที่เห็นใน web.config:
<identity impersonate="true" />
คำสั่งนี้เปิดใช้งานการเลียนแบบฝั่งไคลเอ็นต์ในแอปพลิเคชัน ASP.NET โดยจะแนบโทเค็นการเข้าถึงที่เป็นตัวแทนของไคลเอ็นต์เข้ากับเธรดที่จัดการคำขอ เพื่อให้การตรวจสอบความปลอดภัยที่ดำเนินการโดยระบบปฏิบัติการขัดต่อข้อมูลระบุตัวตนของไคลเอ็นต์ ไม่ใช่ข้อมูลระบุตัวตนของกระบวนการของผู้ปฏิบัติงาน แอปพลิเคชัน ASP.NET แทบไม่จำเป็นต้องมีการเยาะเย้ย ประสบการณ์ของฉันบอกฉันว่านักพัฒนามักจะเปิดใช้งานการเยาะเย้ยด้วยเหตุผลที่ไม่ถูกต้อง นี่คือเหตุผล
นักพัฒนามักเปิดใช้งานการเลียนแบบในแอปพลิเคชัน ASP.NET เพื่อให้สิทธิ์ของระบบไฟล์สามารถใช้เพื่อจำกัดการเข้าถึงเพจได้ หาก Bob ไม่มีสิทธิ์ในการดู Salaries.aspx นักพัฒนาซอฟต์แวร์จะเปิดใช้งานการแอบอ้างบุคคลอื่น เพื่อป้องกันไม่ให้ Bob ดู Salaries.aspx ได้โดยการตั้งค่ารายการควบคุมการเข้าถึง (ACL) เพื่อปฏิเสธสิทธิ์ในการอ่านของ Bob แต่มีอันตรายที่ซ่อนอยู่ดังต่อไปนี้: การแอบอ้างบุคคลอื่นไม่จำเป็นสำหรับการอนุญาต ACL เมื่อคุณเปิดใช้งานการรับรองความถูกต้องของ Windows ในแอปพลิเคชัน ASP.NET ASP.NET จะตรวจสอบ ACL สำหรับแต่ละเพจ .aspx ที่ร้องขอโดยอัตโนมัติ และปฏิเสธคำขอจากผู้โทรที่ไม่มีสิทธิ์ในการอ่านไฟล์ มันยังคงทำงานเช่นนี้แม้ว่าการจำลองจะถูกปิดใช้งานก็ตาม
บางครั้งจำเป็นต้องปรับการจำลองให้เหมาะสม แต่คุณสามารถหลีกเลี่ยงได้ด้วยการออกแบบที่ดี ตัวอย่างเช่น สมมติว่า Salaries.aspx ค้นหาฐานข้อมูลสำหรับข้อมูลเงินเดือนที่ผู้จัดการเท่านั้นที่รู้ ด้วยการเลียนแบบ คุณสามารถใช้สิทธิ์ของฐานข้อมูลเพื่อปฏิเสธความสามารถในการสืบค้นข้อมูลบัญชีเงินเดือนของบุคลากรที่ไม่ใช่ระดับผู้จัดการได้ หรือคุณสามารถละเว้นการแอบอ้างบุคคลอื่นและจำกัดการเข้าถึงข้อมูลบัญชีเงินเดือนโดยการตั้งค่า ACL สำหรับ Salaries.aspx เพื่อให้ผู้ที่ไม่ใช่ผู้ดูแลระบบไม่มีสิทธิ์ในการอ่าน วิธีหลังให้ประสิทธิภาพที่ดีกว่าเพราะหลีกเลี่ยงการเยาะเย้ยโดยสิ้นเชิง นอกจากนี้ยังกำจัดการเข้าถึงฐานข้อมูลที่ไม่จำเป็นอีกด้วย เหตุใดการสืบค้นฐานข้อมูลจึงถูกปฏิเสธเพียงด้วยเหตุผลด้านความปลอดภัย
อย่างไรก็ตาม ฉันเคยช่วยแก้ไขปัญหาแอปพลิเคชัน ASP รุ่นเก่าที่รีสตาร์ทเป็นระยะเนื่องจากหน่วยความจำที่ไม่จำกัด นักพัฒนาที่ไม่มีประสบการณ์แปลงคำสั่ง SELECT เป้าหมายเป็น SELECT * โดยไม่คำนึงว่าตารางที่กำลังสอบถามนั้นมีรูปภาพ ซึ่งมีขนาดใหญ่และจำนวนมาก ปัญหารุนแรงขึ้นจากการรั่วไหลของหน่วยความจำที่ตรวจไม่พบ (พื้นที่โค้ดที่ได้รับการจัดการของฉัน!) แอปพลิเคชันที่ทำงานได้ดีมาหลายปีก็หยุดทำงานกะทันหันเนื่องจากคำสั่ง SELECT ที่เคยส่งคืนข้อมูลหนึ่งกิโลไบต์หรือสองข้อมูลได้ส่งคืนหลายเมกะไบต์แล้ว นอกจากนี้ปัญหาการควบคุมเวอร์ชันที่ไม่เพียงพอ และชีวิตของทีมพัฒนาจะต้อง "กระทำมากกว่าปก" — และโดย "ซึ่งกระทำมากกว่าปก" มันเหมือนกับการดูลูก ๆ ของคุณเล่นเกมที่น่ารำคาญในขณะที่คุณกำลังจะ นอนตอนกลางคืน เกมฟุตบอลที่น่าเบื่อ
ตามทฤษฎีแล้ว หน่วยความจำรั่วแบบเดิมไม่สามารถเกิดขึ้นในแอปพลิเคชัน ASP.NET ที่ประกอบด้วยโค้ดที่ได้รับการจัดการทั้งหมด แต่การใช้หน่วยความจำไม่เพียงพออาจส่งผลต่อประสิทธิภาพการทำงานได้โดยการบังคับให้การรวบรวมขยะเกิดขึ้นบ่อยขึ้น แม้แต่ในแอปพลิเคชัน ASP.NET โปรดระวัง SELECT *!
กลับไปด้านบน อย่าพึ่งใช้มันเลย — ตั้งค่าไฟล์คอนฟิกูเรชันของฐานข้อมูล!
ในฐานะที่ปรึกษา ฉันมักถูกถามว่าทำไมแอปพลิเคชันจึงไม่ทำงานตามที่คาดไว้ เมื่อเร็วๆ นี้ มีคนถามทีมของฉันว่าเหตุใดแอปพลิเคชัน ASP.NET จึงเสร็จสิ้นเพียงประมาณ 1/100 ของปริมาณงาน (คำขอต่อวินาที) ที่จำเป็นในการขอเอกสาร ปัญหาที่เราพบก่อนหน้านี้เป็นปัญหาเฉพาะของปัญหาที่เราพบในเว็บแอปพลิเคชันที่ทำงานไม่ถูกต้อง และเป็นบทเรียนที่เราทุกคนควรให้ความสำคัญอย่างจริงจัง
เราเรียกใช้ SQL Server Profiler และตรวจสอบการโต้ตอบระหว่างแอปพลิเคชันนี้กับฐานข้อมูลส่วนหลัง ในกรณีที่รุนแรงกว่านั้น การคลิกเพียงปุ่มเดียวทำให้เกิดข้อผิดพลาดมากกว่า 1,500 รายการในฐานข้อมูล คุณไม่สามารถสร้างแอพพลิเคชั่นประสิทธิภาพสูงได้ สถาปัตยกรรมที่ดีมักจะเริ่มต้นด้วยการออกแบบฐานข้อมูลที่ดี ไม่ว่ารหัสของคุณจะมีประสิทธิภาพเพียงใดมันจะไม่ทำงานหากมีการชั่งน้ำหนักโดยฐานข้อมูลที่เขียนไม่ดี
สถาปัตยกรรมการเข้าถึงข้อมูลที่ไม่ดีมักเกิดจากสิ่งต่อไปนี้อย่างน้อยหนึ่งอย่าง:
•การออกแบบฐานข้อมูลที่ไม่ดี (โดยปกติแล้วจะออกแบบโดยนักพัฒนาไม่ใช่ผู้ดูแลระบบฐานข้อมูล)
•การใช้ชุดข้อมูลและ DataAdapters - โดยเฉพาะ DataAdapter.Update ซึ่งทำงานได้ดีสำหรับแอปพลิเคชัน Windows และไคลเอนต์ที่หลากหลายอื่น ๆ แต่โดยทั่วไปไม่เหมาะสำหรับเว็บแอปพลิเคชัน
•เลเยอร์การเข้าถึงข้อมูลที่ออกแบบมาไม่ดี (DAL) ที่มีการคำนวณโปรแกรมไม่ดีและใช้ CPU รอบหลายรอบเพื่อดำเนินการค่อนข้างง่าย
ต้องระบุปัญหาก่อนที่จะสามารถรักษาได้ วิธีการระบุปัญหาการเข้าถึงข้อมูลคือการเรียกใช้ SQL Server Profiler หรือเครื่องมือเทียบเท่าเพื่อดูว่าเกิดอะไรขึ้นเบื้องหลัง การปรับแต่งประสิทธิภาพเสร็จสมบูรณ์หลังจากตรวจสอบการสื่อสารระหว่างแอปพลิเคชันและฐานข้อมูล ลองดู - คุณอาจจะประหลาดใจกับสิ่งที่คุณพบ
กลับไปสู่ข้อสรุปด้านบนตอนนี้คุณรู้ปัญหาบางอย่างและวิธีแก้ปัญหาที่คุณอาจพบเมื่อสร้างแอปพลิเคชันการผลิต ASP.NET ขั้นตอนต่อไปคือการดูรหัสของคุณเองอย่างใกล้ชิดและพยายามหลีกเลี่ยงปัญหาบางอย่างที่ฉันได้ระบุไว้ที่นี่ ASP.NET อาจลดอุปสรรคในการเข้าสู่นักพัฒนาเว็บ แต่แอปพลิเคชันของคุณมีเหตุผลทุกประการที่จะยืดหยุ่นมั่นคงและมีประสิทธิภาพ โปรดพิจารณาสิ่งนี้อย่างระมัดระวังเพื่อหลีกเลี่ยงข้อผิดพลาดเริ่มต้น
รูปที่ 8 แสดงรายการตรวจสอบสั้น ๆ ที่คุณสามารถใช้เพื่อหลีกเลี่ยงข้อผิดพลาดที่อธิบายไว้ในบทความนี้ คุณสามารถสร้างรายการตรวจสอบข้อบกพร่องด้านความปลอดภัยที่คล้ายกัน ตัวอย่างเช่น:
•คุณได้เข้ารหัสส่วนการกำหนดค่าที่มีข้อมูลที่ละเอียดอ่อนหรือไม่?
•คุณกำลังตรวจสอบและตรวจสอบความถูกต้องของอินพุตที่ใช้ในการดำเนินการฐานข้อมูลและคุณใช้อินพุตที่เข้ารหัส HTML เป็นเอาต์พุตหรือไม่?
•ไดเรกทอรีเสมือนของคุณมีไฟล์ที่มีส่วนขยายที่ไม่มีการป้องกันหรือไม่?
คำถามเหล่านี้มีความสำคัญหากคุณให้ความสำคัญกับความสมบูรณ์ของเว็บไซต์เซิร์ฟเวอร์ที่โฮสต์และแหล่งข้อมูลแบ็กเอนด์ที่พวกเขาพึ่งพา
Jeff Prosise เป็นบรรณาธิการที่มีส่วนร่วมในนิตยสาร MSDN และผู้เขียนหนังสือหลายเล่มรวมถึงการเขียนโปรแกรม Microsoft .NET (Microsoft Press, 2002) เขายังเป็นผู้ร่วมก่อตั้ง Wintellect ซึ่งเป็น บริษัท ที่ปรึกษาด้านซอฟต์แวร์และการศึกษา
จากนิตยสาร MSDN ฉบับเดือนกรกฎาคม 2549