< rewriter >
< เขียนใหม่ url = " ^/ผู้ใช้/(d+)$ " ถึง = " ~/User.aspx?id=$1 " การประมวลผล = " หยุด " />
< เขียนใหม่ url = " ^/ผู้ใช้/(w+)$ " ถึง = " ~/User.aspx?name=$1 " การประมวลผล = " หยุด " />
</ เขียนใหม่ >
เมื่อผู้ใช้ร้องขอ "/User/jeffz" โค้ดที่ปรากฏบนเพจจะเป็น <form action="/User.aspx?name=jeffz" /> เนื่องจากเมื่อโค้ดถูกสร้างขึ้น หน้า ค่าปัจจุบันของ Request.Url.PathAndQuery จะถูกใช้เพื่อรับการทำงานขององค์ประกอบแบบฟอร์ม ซึ่งจะทำให้ "User.aspx?name=jeffz" ปรากฏในแถบที่อยู่เมื่อ PostBack และที่อยู่นี้อาจไม่สามารถขอทรัพยากรที่ถูกต้องได้ (เนื่องจากอาจถูกเขียนใหม่ในที่อื่น หรือเนื่องจากความสัมพันธ์ระดับไดเรกทอรี) ไม่มี ทรัพยากรดังกล่าว) ในบทความก่อนหน้านี้ " UpdatePanel และ UrlRewrite " ฉันบอกว่าปัญหานี้สามารถแก้ไขได้โดยการเพิ่มบรรทัดโค้ด JavaScript ที่ท้ายหน้า:
< script language ="javascript" type ="text/javascript">
document.getElementsByTagName( "แบบฟอร์ม" )[0].action = window.location;
</script> วัตถุประสงค์ ของ
บรรทัดโค้ดนี้ชัดเจนมาก เปลี่ยนการกระทำของแบบฟอร์มเป็น window.location (นั่นคือเส้นทางในแถบที่อยู่ของเบราว์เซอร์) เพื่อว่าเมื่อเพจดำเนินการ PostBack ที่อยู่เป้าหมายจะ เป็นที่อยู่ก่อนเขียน URL ใหม่ วิธีนี้สามารถทำให้โปรแกรมทำงานได้ตามปกติ แต่ก็ไม่ได้ทำให้ฉันพอใจจริงๆ ทำไม
เพราะมันน่าเกลียดเกินไป
เนื่องจากเรายังคงเปิดเผยที่อยู่หลังจากเขียน URL ใหม่ให้กับลูกค้า ตราบใดที่ผู้ใช้ติดตั้ง HTTP sniffer (เช่น Fiddler ที่มีชื่อเสียง) หรือเลือกโดยตรงเพื่อดูไฟล์ต้นฉบับใน IE ที่อยู่เป้าหมายของเราจะแสดงต่อหน้าผู้ใช้โดยไม่มีการปกปิดใดๆ เราจะแจ้งให้ผู้ใช้ทราบกฎการเขียนใหม่ของเราได้อย่างไร เราต้องแก้ปัญหานี้ วิธีแก้ปัญหานั้นง่ายมากและได้รับความนิยมอย่างมากอยู่แล้ว ซึ่งก็คือการใช้ Control Adapter เพื่อเปลี่ยนลักษณะการทำงานของการสร้างแบบฟอร์ม แต่สิ่งที่ทำให้ฉันรู้สึกแปลกก็คือการค้นหา Control Adapter นี้บนอินเทอร์เน็ตทั้งหมดเป็นเวอร์ชัน VB.NET แต่ไม่พบภาษา C# ที่ Microsoft ส่งเสริมเป็นหลัก แม้ว่าการเขียนใหม่ไม่ใช่เรื่องยากตราบใดที่คุณเข้าใจไวยากรณ์ VB.NET เพียงเล็กน้อย แต่ก็ยังมีงานพิเศษอยู่ ดังนั้นผมจะโพสต์โค้ดเวอร์ชัน C# ของ Adapter นี้ทันที เพื่อให้เพื่อนๆ สามารถใช้งานได้โดยตรง:
namespace Sample.Web.UI.Adapters
-
FormRewriterControlAdapter คลาส สาธารณะ :
System.Web.UI.Adapters.ControlAdapter
-
ป้องกัน แทนที่ void Render (ผู้เขียน HtmlTextWriter )
-
ฐาน .Render ( RewriteFormHtmlTextWriter ใหม่ (ผู้เขียน));
-
-
คลาส สาธารณะ RewriteFormHtmlTextWriter : HtmlTextWriter
-
RewriteFormHtmlTextWriter สาธารณะ (นักเขียน HtmlTextWriter )
: เบส (นักเขียน)
-
นี้ .InnerWriter =writer.InnerWriter;
-
RewriteFormHtmlTextWriter สาธารณะ (นักเขียน TextWriter )
: เบส (นักเขียน)
-
นี้ .InnerWriter = นักเขียน;
-
การแทนที่ สาธารณะ เป็น โมฆะ WriteAttribute (ชื่อ สตริง , ค่า สตริง , บูล fEncode)
-
ถ้า (ชื่อ == "การกระทำ" )
-
บริบท HttpContext = HttpContext .ปัจจุบัน;
ถ้า (context.Items[ "ActionAlreadyWritten" ] == null )
-
ค่า = context.Request.RawUrl;
context.Items[ "ActionAlreadyWritten" ] = จริง ;
-
-
ฐาน .WriteAttribute (ชื่อ, ค่า, fEncode);
-
-
}
พูดง่ายๆ ก็คือ Control Adapter นี้กำลังรอช่วงเวลาที่แอตทริบิวต์ "action" ถูกส่งออก และเปลี่ยนค่าเป็นแอตทริบิวต์ RawUrl ของวัตถุคำขอปัจจุบัน คุณลักษณะนี้ถูกกำหนดเมื่อ ASP.NET ได้รับการร้องขอจาก IIS เป็นครั้งแรก ซึ่งจะไม่เปลี่ยนแปลงกับการดำเนินการเขียนซ้ำที่ตามมาใน BeginRequest ดังนั้น เราจำเป็นต้องส่งออก RawUrl สำหรับการดำเนินการของแบบฟอร์มเพื่อแก้ไขปัญหาการเปลี่ยนแปลงที่อยู่ PostBack
อย่างไรก็ตาม เพื่อให้อะแดปเตอร์ควบคุมนี้มีผลใช้งาน คุณต้องสร้างไฟล์เบราว์เซอร์ในโครงการเว็บ เช่น "App_BrowsersForm.browser" และเขียนโค้ดต่อไปนี้ในนั้น:
< browsers >
< เบราว์เซอร์ refID = " ค่าเริ่มต้น " >
<ตัวควบคุมอะแดปเตอร์>
< อะแดปเตอร์ controlType = " System.Web.UI.HtmlControls.HtmlForm "
adapterType = " Sample.Web.UI.Adapters.FormRewriterControlAdapter " />
</ ควบคุมอะแดปเตอร์ >
</ เบราว์เซอร์ >
</ เบราว์เซอร์ >
ณ จุดนี้ ปัญหาการเปลี่ยนแปลงที่อยู่ PostBack ที่เกิดจากการเขียน URL ซ้ำที่ระดับ ASP.NET ได้รับการแก้ไขอย่างสมบูรณ์แบบ - เดี๋ยวก่อน ทำไมเราจึงควรเน้นที่ "ระดับ ASP.NET" ถูกต้อง เพราะถ้าคุณเขียน URL Rewrite ในระดับ IIS ปัญหานี้ยังคงมีอยู่ ตัวอย่างเช่น หากคุณใช้ IIRF สำหรับการเขียน URL ใหม่ และเปิดใช้งานอะแดปเตอร์ควบคุมข้างต้น คุณจะยังคงพบว่าที่อยู่ PostBack บนเพจแตกต่างจากที่อยู่ที่ร้องขอโดยไคลเอ็นต์ RawUrl กลายเป็น "ไม่ซื่อสัตย์" หรือไม่? สิ่งนี้ไม่ได้เกิดจาก RawUrl แต่ถูกกำหนดโดยกลไก ASP.NET เพื่ออธิบายปัญหานี้ เรามาดูแผนภาพในบทความแรก " IIS และ ASP.NET " อีกครั้ง:
การเขียนซ้ำ URL ระดับ IIS เกิดขึ้นก่อนขั้นตอนที่ 2 ในภาพด้านบน เนื่องจากมีการเขียนใหม่ ตัวเลือก ISAPI ของ IIS จะส่งมอบคำขอไปยัง ASPNET ISAPI สำหรับการประมวลผล กล่าวอีกนัยหนึ่ง เมื่อ IIS ส่งคำขอไปยังกลไก ASP.NET เพื่อประมวลผล ข้อมูลที่ ASP.NET ที่ได้รับจาก IIS จะเป็นที่อยู่อยู่แล้วหลังจากการเขียน URL ใหม่ (เช่น /User.aspx?name=jeffz) ดังนั้นไม่ว่า ไม่ว่า ASP.NET จะประมวลผลคำขอจากที่ใด ก็ไม่สามารถทราบ URL ได้เมื่อ IIS ได้รับการร้องขอ
กล่าวอีกนัยหนึ่ง เราไม่สามารถทำอะไรกับเรื่องนี้ได้จริงๆ
อย่างไรก็ตาม คำสี่คำที่ว่า "ไม่มีทางเลย" นั้นเป็นเงื่อนไข กล่าวโดยสมบูรณ์ ควรเป็น: "การพึ่งพา ASP.NET เอง" ย่อมเป็น "ไม่มีทาง" อย่างแน่นอน แต่จะเกิดอะไรขึ้นถ้า IIS ช่วยเราในการเขียน URL ใหม่ เนื่องจากเป็นองค์ประกอบโอเพ่นซอร์สที่สมบูรณ์ IIRF จึงรู้ดีอยู่แล้วว่ากลไก ASP.NET และแม้แต่ตัวจัดการ ISAPI ทั้งหมดก็ต้องการความช่วยเหลือ โดยธรรมชาติแล้วจะรู้หลักการของ "ทำการเปลี่ยนแปลงเมื่อคุณทำการเปลี่ยนแปลง" ดังนั้นจึงได้เรียนรู้ที่จะจัดเก็บที่อยู่ดั้งเดิม ความสามารถในตัวแปรเซิร์ฟเวอร์ HTTP_X_REWRITE_URL อย่างไรก็ตาม IIRF จะไม่ทำสิ่งนี้ "อย่างมีสติ" (เหนื่อยแค่ไหน) ดังนั้นเราจึงจำเป็นต้องเตือนมันในไฟล์กำหนดค่า:
RewriteRule ^/User/(d+)$ /User.aspx?id=$1 [I, L , U ]
RewriteRule ^/User/(w+)$ /User.aspx?name=$1 [I, L, U]
โปรดทราบว่าเราใช้ตัวแก้ไขเพิ่มเติม การเพิ่ม U ลงในคอลเลกชัน Modifier บ่งชี้ว่าเราต้องใช้ IIRF เพื่อจัดเก็บที่อยู่เดิมก่อนที่จะเขียน URL ใหม่ในตัวแปรเซิร์ฟเวอร์ HTTP_X_REWRITE_URL ตอนนี้เราสามารถรับค่านี้ใน ASP.NET ได้ ดังนั้นเราจึงแก้ไขวิธี WriteAttribute ในโค้ด Control Adapter ก่อนหน้านี้ดังนี้:
public override void WriteAttribute( string name, string value, bool fEncode)
-
ถ้า (ชื่อ == "การกระทำ" )
-
บริบท HttpContext = HttpContext .ปัจจุบัน;
ถ้า (context.Items[ "ActionAlreadyWritten" ] == null )
-
ค่า = context.Request.ServerVariables [ "HTTP_X_REWRITE_URL" ]
?? บริบท.Request.RawUrl;
context.Items[ "ActionAlreadyWritten" ] = จริง ;
-
-
ฐาน .WriteAttribute (ชื่อ, ค่า, fEncode);
}
ตอนนี้ค่าของการดำเนินการไม่ได้มาจากแอตทริบิวต์ RawUrl เท่านั้น แต่พยายามรับค่าของตัวแปร HTTP_X_REWRITE_URL จากคอลเลกชัน ServerVariables เนื่องจากที่อยู่ของคำขอดั้งเดิมที่ได้รับโดย IIS ถูกเก็บไว้ที่นั่น
ณ จุดนี้ เราได้กล่าวถึงหัวข้อหลักเกี่ยวกับการเขียน URL ใหม่แล้ว ในบทความถัดไปซึ่งเป็นบทความสุดท้ายของชุดนี้ เราจะเน้นไปที่ความแตกต่างในรายละเอียดบางอย่างที่เกิดจากการใช้การเขียน URL ในระดับต่างๆ และประเด็นที่เกี่ยวข้องของ บันทึก.