การแนะนำ
เทคโนโลยีที่ใช้
การวิเคราะห์ข้อกำหนด
รูปแบบข้อมูล
สถาปัตยกรรมแอปพลิเคชัน
ชั้นข้อมูล
ผู้ควบคุม
ตัวจัดการการกระทำ
มุมมอง
ตัวกรอง
ผลลัพธ์
แอปพลิเคชันเว็บที่ใช้ Java สำหรับกิจกรรมสิ่งที่ต้องทำ ผ่านเว็บแอปพลิเคชันนี้เราสามารถสร้างอ่านและอัปเดต TODOS ของพวกเขาผ่านเว็บเบราว์เซอร์ที่ทันสมัย แอปพลิเคชันยังใช้ AAA ซึ่งหมายความว่าผู้ใช้ทุกคนมีบัญชีของตนเองและรายการสิ่งที่ต้องทำ ฯลฯ เป็นส่วนตัวสำหรับพวกเขา
เอกสารนี้ไม่ได้มีไว้สำหรับมือใหม่ คุณต้องมีความรู้ที่ดีเกี่ยวกับเทคโนโลยีด้านล่างเพื่อทำความเข้าใจเอกสารนี้และแอปพลิเคชันที่เกี่ยวข้อง:
ชวา
servlet, jsp
Apache Tomcat
mysql
HTML
Apache netbeans ide
Firefox
นี่เป็นโครงการแบ็คเอนด์ทั้งหมด ดังนั้นเทคโนโลยีส่วนหน้าเช่น CSS ไม่ได้ใช้ JavaScript เป้าหมายของโครงการคือการเรียนรู้และแสดงอย่างมีประสิทธิภาพว่าชิ้นส่วนของ Java Servlet API ทำงานร่วมกันอย่างไร
เราจะพัฒนาเว็บแอปพลิเคชันเริ่มต้นด้วยการวิเคราะห์ข้อกำหนด จากนั้นเราจะไปยังการออกแบบฐานข้อมูล ข้อมูลเป็นศูนย์กลางของเว็บแอปพลิเคชันใด ๆ เกือบทุกกรณีการใช้งานจัดการกับข้อมูล เมื่อโมเดลข้อมูลของเว็บแอปพลิเคชันพร้อมแล้วเราจะไปยังการออกแบบสถาปัตยกรรมของแอปพลิเคชัน ในขั้นตอนนี้เราจะเห็นว่าแอปพลิเคชันของเราทำงานอย่างไรกับการกระทำ HTTP ที่แตกต่างกันอย่างไร เพราะการกระทำทั้งหมดที่ดำเนินการโดยผู้ใช้ของแอปพลิเคชันจะผ่าน HTTP เราจะคิดถึงการกระทำของผู้ใช้ที่เป็นไปได้ทั้งหมดและกำหนดให้ชัดเจน ต่อไปเราจะไปยังการออกแบบอินเทอร์เฟซและคลาส
สำหรับแอปพลิเคชันของเราเราเริ่มต้นด้วยการกำหนดสิ่งที่สิ่งที่ต้องทำสำหรับเรา สิ่งที่ต้องทำเป็นงานที่ต้องสำเร็จ เราสร้างรายการงานดังกล่าวเพื่อช่วยให้เราใช้ชีวิตอย่างต่อเนื่อง เราติดตามรายการต่อไปในขณะที่เราทำงานให้เสร็จ รายการสิ่งที่ต้องทำสำหรับเรามีคุณสมบัติด้านล่าง:
เริ่มแรกงานจะมีสถานะ 'todo' เมื่อเราเริ่มทำงานเราจะเปลี่ยนเป็น 'กำลังดำเนินการ' เมื่องานสำเร็จเราจะทำเครื่องหมายสถานะของมันว่า 'เสร็จสิ้น'
เราต้องการให้แอปพลิเคชันของเรารองรับผู้ใช้หลายคน และผู้ใช้ทุกคนจะมีรายการส่วนตัวของตนเอง ดังนั้นผู้ใช้จะไม่เห็นรายการสิ่งที่ต้องทำของผู้อื่น ผู้ใช้จะต้องระบุชื่อผู้ใช้ซึ่งเป็นที่อยู่อีเมลที่ถูกต้องสำหรับเรา ผู้ใช้จะได้รับบัญชีในใบสมัครของเรา ดังนั้นบัญชีมีคุณสมบัติด้านล่าง:
เราต้องการบัญชี 'ผู้ดูแลระบบ' เพื่อจัดการบัญชีเท่านั้น บัญชีผู้ดูแลระบบจะใช้ชื่อผู้ใช้ 'ผู้ดูแลระบบ' ผู้ใช้ Admin สามารถ:
สองรายการสุดท้ายมีค่าควรสังเกต โดยปกติแล้วจะเชื่อว่าผู้ใช้ที่มีสิทธิ์ 'ผู้ดูแลระบบ' สามารถเข้าถึงข้อมูลของทุกคนได้ เราไม่ต้องการเช่นนั้น นอกจากนี้เราได้กำหนดไว้แล้วว่าบัญชี 'ผู้ดูแลระบบ' สำหรับเรานั้นเป็นเพียงการจัดการบัญชีเท่านั้น ไม่ใช่สำหรับการจัดการรายชื่อผู้ใช้ บัญชีผู้ใช้ 'Admin' ไม่ได้ใช้บ่อย มันมีความหมายสำหรับวัตถุประสงค์พิเศษเท่านั้น สำหรับแอปพลิเคชันของเราเราคาดว่าบัญชีผู้ใช้หนึ่งบัญชีจะจัดการบัญชี 'ผู้ดูแลระบบ' ด้วย ดังนั้นมันจะเป็นบุคคลเดียวกันที่เข้าสู่ระบบโดยใช้ข้อมูลรับรอง 'ผู้ดูแลระบบ' เมื่อจำเป็นเท่านั้น เนื่องจากเป็นบัญชีผู้ใช้ที่มีอยู่โดยใช้บัญชี 'ผู้ดูแลระบบ' เพื่อจัดการบัญชีทั้งหมดเราจึงไม่ต้องการรายการงานแยกต่างหากสำหรับบัญชีผู้ดูแลระบบ ที่ไม่ได้มีจุดประสงค์ใด ๆ
เราต้องการให้รายการสิ่งที่ต้องทำอยู่เสมอ ซึ่งหมายความว่าเมื่อผู้ใช้สร้างรายการสิ่งที่ต้องทำสำเร็จแล้วจะไม่สามารถลบได้ ในทำนองเดียวกันเราไม่ต้องการลบบัญชีผู้ใช้ โดยสรุปเราไม่ต้องการสนับสนุนการดำเนินการ 'ลบ' ในแอปพลิเคชันของเรา ดังนั้นเราจึงสนับสนุน Cru จาก Crud เท่านั้น
เนื่องจากเราต้องการให้แอปพลิเคชันของเรารักษารายการสิ่งที่ต้องทำส่วนตัวเราต้องการให้แอปพลิเคชันจัดเตรียมกลไกการเข้าสู่ระบบและการออกจากระบบ สิ่งนี้เรียกว่า 'การรับรองความถูกต้อง' ผู้ใช้ทุกคนรวมถึง 'ผู้ดูแลระบบ' ควรตรวจสอบตัวเองก่อน เมื่อการรับรองความถูกต้องที่ประสบความสำเร็จผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังพื้นที่ทำงานของพวกเขา เนื่องจากเรากำลังพูดถึงผู้ใช้สองประเภท (ผู้ดูแลระบบหนึ่งคนและอื่น ๆ ปกติ) เราจะมีพื้นที่ทำงานสองประเภทในแอปพลิเคชันของเรา ผู้ใช้งานผู้ดูแลระบบจะทำงานกับพื้นที่ทำงานการจัดการบัญชีผู้ใช้เท่านั้น ผู้ใช้ปกติจะต้องทำงานกับพื้นที่ทำงานของ ToDo List Management เท่านั้น ทั้งคู่เป็นเอกสิทธิ์ ผู้ใช้ปกติไม่สามารถเห็นพื้นที่ทำงานของผู้ดูแลระบบ และผู้ใช้ Admin ไม่สามารถเห็นพื้นที่ทำงานของผู้ใช้ปกติ สิ่งนี้เรียกว่า 'การอนุญาต'
นอกเหนือจากข้อกำหนดข้างต้นเราต้องการให้แอปพลิเคชันของเราจัดเก็บรายละเอียดของการเข้าสู่ระบบของผู้ใช้และการประทับเวลาออกจากระบบ ผ่านสิ่งนี้เรากำลังติดตามกิจกรรมของผู้ใช้ในแอปพลิเคชันของเรา นี่ไม่ใช่ 'การบัญชี' อย่างแน่นอนจาก AAA แต่สำหรับแอปพลิเคชันของเราสิ่งนี้มีจุดประสงค์ในการเรียกมันว่า 'การบัญชี'
ตามข้อกำหนดที่เรารวบรวมมาแล้วเราเข้าใจว่าเราต้องจัดเก็บข้อมูลสำหรับเอนทิตีด้านล่างของแอปพลิเคชัน:
ตัวอย่างข้อมูลสำหรับบัญชีไม่กี่บัญชี:
| รหัสบัญชี | ชื่อผู้ใช้ | ชื่อแรก | นามสกุล | รหัสผ่าน | สร้างขึ้นที่ | สถานะ |
|---|---|---|---|---|---|---|
| 1 | ผู้ดูแลระบบ | ผู้ดูแลระบบ | ผู้ใช้ | รหัสผ่าน | 2020-05-06 17:34:04 | เปิดใช้งาน |
| 2 | [email protected] | จอห์น | จอห์นส์สัน | Oneword | 2020-05-07 12:34:04 | พิการ |
| 3 | [email protected] | เอริค | Ericsson | Twoword | 2020-05-08 13:34:04 | เปิดใช้งาน |
| 4 | [email protected] | อานา | แมรี่ | สามคำ | 2020-05-09 11:34:04 | เปิดใช้งาน |
เราเห็นว่าสถานะบัญชีซ้ำตลอดตาราง ดังนั้นในฐานะที่เป็นส่วนหนึ่งของการทำให้เป็นมาตรฐานฐานข้อมูลควรใส่ข้อมูลซ้ำ ๆ ในตารางแยกต่างหาก มีเหตุผลที่ดีอยู่เบื้องหลัง สมมติว่าเรามีผู้ใช้ 100 คน และเราต้องการแทนที่คำที่เปิดใช้งานและปิดใช้งานด้วย 1 และ 2 ตามลำดับ เราต้องแก้ไขคอลัมน์สถานะของทุกแถวของตาราง ลองนึกภาพว่ามันยุ่งยากแค่ไหนที่จะทำการดัดแปลงเช่นนี้สำหรับตารางที่มีแถวนับพัน! การทำให้เป็นมาตรฐานฐานข้อมูลที่ Rescue ขอบคุณ!
หลังจากการทำให้เป็นมาตรฐานเราจะมีสองตาราง - Account_statuses และบัญชี:
Account_statuses
| รหัสประจำตัว | สถานะ |
|---|---|
| 1 | เปิดใช้งาน |
| 2 | พิการ |
บัญชี
| รหัสบัญชี | ชื่อผู้ใช้ | ชื่อแรก | นามสกุล | รหัสผ่าน | รหัสสถานะ |
|---|---|---|---|---|---|
| 1 | ผู้ดูแลระบบ | ผู้ดูแลระบบ | ผู้ใช้ | รหัสผ่าน | 1 |
| 2 | [email protected] | จอห์น | จอห์นส์สัน | Oneword | 2 |
| 3 | [email protected] | เอริค | Ericsson | Twoword | 1 |
| 4 | [email protected] | อานา | แมรี่ | สามคำ | 2 |
ในทำนองเดียวกันเราจะมีสามตารางสำหรับงาน - task_statuses, task_priorities และงาน:
task_statuses
| รหัสประจำตัว | สถานะ |
|---|---|
| 1 | สิ่งที่ต้องทำ |
| 2 | การดำเนินการ |
| 3 | เสร็จแล้ว |
task_priorities
| รหัสประจำตัว | ลำดับความสำคัญ |
|---|---|
| 1 | สำคัญและเร่งด่วน |
| 2 | สำคัญ แต่ไม่เร่งด่วน |
| 3 | ไม่สำคัญ แต่เร่งด่วน |
| 4 | ไม่สำคัญและไม่เร่งด่วน |
งาน
| รหัสงาน | รหัสบัญชี | รายละเอียด | สร้างขึ้นที่ | เส้นตาย | อัปเดตล่าสุด | รหัสสถานะ | รหัสลำดับความสำคัญ |
|---|---|---|---|---|---|---|---|
| 1 | 2 | ซื้อดินสอ | 2019-05-06 17:40:03 | 2019-05-07 17:40:03 | 2 | 1 | |
| 2 | 3 | ซื้อหนังสือ | 2019-05-07 7:40:03 | 2019-05-07 17:40:03 | 2019-05-07 23:40:03 | 2 | 1 |
ในที่สุดเราก็มีข้อกำหนดอื่นในการจัดเก็บข้อมูลเซสชันบัญชี เราจะเก็บไว้ตามที่แสดงในตารางด้านล่าง:
Account_sessions
| รหัสเซสชัน | รหัสบัญชี | เซสชันที่สร้างขึ้น | ตอนจบเซสชัน |
|---|---|---|---|
| ASD1GH | 1 | 2019-05-06 17:40:03 | 2019-05-06 18:00:03 |
โดยปกติในรหัสแอปพลิเคชันขององค์กรจะไม่ถูกจัดเก็บเป็นจำนวนเต็ม เพราะมันจะง่ายขึ้นสำหรับคนที่จะสอบถามข้อมูลอื่น ๆ โดยใช้จำนวนเต็ม! ในแอพพลิเคชั่นในโลกแห่งความเป็นจริง ID ไม่ใช่ตัวเลข แต่เป็นตัวอักษรและตัวเลขโดยมีอักขระมากถึง 100 อักขระ ดังนั้นทำให้เป็นไปไม่ได้ที่ใครบางคนจะเดา ID อื่น!
เราจะเรียกฐานข้อมูลของเราว่า 'tode' ใน MySQL และนี่คือรูปแบบข้อมูลที่สร้างขึ้นตามข้อมูลข้างต้น:

เราจะพัฒนาแอปพลิเคชันนี้ตามรูปแบบ MVC 2 Desgin ที่มีชื่อเสียงและใช้กันอย่างแพร่หลาย รูปด้านล่างแสดงให้เห็นว่าเราจะใช้ MVC สำหรับแอปของเราได้อย่างไร: 
แอปพลิเคชันของเราจะดำเนินการตาม เมื่อผู้ใช้ส่งคำขอ HTTP ไปยังแอปพลิเคชันของเราเราจะแปลเป็นการกระทำที่สอดคล้องกันในแอปพลิเคชันของเรา การกระทำที่เราสนับสนุนคือการสร้างอ่านและอัปเดต (CRU) แอปพลิเคชันของเราเป็นหลักคือข้อมูลที่ขับเคลื่อน มันอำนวยความสะดวกในการกระทำในฐานข้อมูล ช่วยให้ผู้ใช้สามารถจัดเก็บและจัดการข้อมูลของพวกเขาในฐานข้อมูลระยะไกลอย่างปลอดภัยและปลอดภัยด้วยความช่วยเหลือของกลไกการรับรองความถูกต้องและการอนุญาต มันทำหน้าที่เป็นอินเทอร์เฟซที่ใช้ HTML และ HTTP ไปยังฐานข้อมูล
เมื่อผู้ใช้ทำคำขอ HTTP ไปยังแอปพลิเคชันของเราเราจะส่งข้อมูลที่ร้องขอกลับในรูปแบบของ HTML HTML รองรับลิงก์และแบบฟอร์มเพื่อช่วยให้ผู้ใช้โต้ตอบกับเว็บแอปพลิเคชัน ลิงก์ใช้เพื่อดึง/รับ (HTTP GET) ข้อมูลในขณะที่แบบฟอร์มจะใช้ในการโพสต์ข้อมูล (โพสต์ HTTP) ไปยังเว็บแอปพลิเคชัน
ดังนั้นนี่คือวิธีที่เราจะแปลคำขอ HTTP เป็นการกระทำ:
| องค์ประกอบ HTML | วิธี http | การดำเนินการของแอปพลิเคชัน |
|---|---|---|
| การเชื่อมโยงหลายมิติ | http get | อ่านรายละเอียด |
| รูปร่าง | โพสต์ http | สร้างหรืออัปเดต |
HTTP Get ส่งข้อมูลเป็นพารามิเตอร์การสืบค้นไปยัง URL ในขณะที่โพสต์ HTTP ส่งข้อมูลในร่างกายคำขอ HTTP โพสต์ HTTP ไม่เปิดเผยข้อมูลผ่าน URL ในขณะที่ HTTP Get ทำ ดังนั้น HTTP GET จึงไม่เหมาะสำหรับการส่งข้อมูลรับรองการเข้าสู่ระบบ ไม่มีใครอยากเห็นชื่อผู้ใช้และรหัสผ่านต่อท้ายเข้ากับ URL คำขอ HTTP! เราจะใช้โพสต์ HTTP เพื่อส่งข้อมูลรับรองของผู้ใช้ในขณะที่เข้าสู่ระบบ
ดังนั้นเราได้ตัดสินใจว่าเราจะใช้ HTTP, HTML และฐานข้อมูลอย่างไร มีแนวคิดอีกอย่างหนึ่งของ HTTP ที่จำเป็นสำหรับเราที่จะต้องเข้าใจในการตัดสินใจสถาปัตยกรรมของแอปพลิเคชันที่ใช้ HTTP ของเรา นั่นคือ URL - ตัวระบุตำแหน่งทรัพยากรที่เหมือนกัน นี่คือตัวอย่าง URL ของเว็บแอปพลิเคชันที่เรียกว่า 'WebApp' ที่โฮสต์บนเซิร์ฟเวอร์ตัวอย่าง:
http://www.example.com/webapp/details?id=12
ใน URL ตัวอย่างข้างต้น 'http' คือโปรโตคอล, www.example.com เป็นชื่อโดเมนหรือชื่อเซิร์ฟเวอร์ 'WebApp' เป็นบริบทแอปพลิเคชันที่ปรับใช้บนเซิร์ฟเวอร์และ 'รายละเอียด' เป็นแอปพลิเคชันที่เรากำลังส่งคำขอ HTTP ของเรา 'ID' เป็นพารามิเตอร์การสืบค้นที่เราส่งผ่านไปยัง 'รายละเอียด' ด้วยค่า '12' พารามิเตอร์การสืบค้นช่วยให้เรามีกลไกในการส่งพารามิเตอร์ไปยังเว็บแอปพลิเคชันและรับเนื้อหาที่เกี่ยวข้องกลับมาตอบกลับจากเว็บแอปพลิเคชัน ตัวอย่างเช่นลองนึกภาพแอปพลิเคชันสภาพอากาศที่ทำงานบนเว็บเซิร์ฟเวอร์ แทนที่จะจัดทำรายการรายงานสภาพอากาศให้กับเราในสถานที่ทั้งหมดเราสามารถส่งตัวเลือกตำแหน่งของเราเป็นพารามิเตอร์การสืบค้นไปยังเว็บแอปพลิเคชัน แอปพลิเคชันจะส่งรายละเอียดสภาพอากาศตามที่เลือกของเรา
เว็บแอปพลิเคชันทำงานบนเว็บเซิร์ฟเวอร์ซึ่งแตกต่างจากแอปพลิเคชันที่ทำงานบนพีซีของเรา แอปพลิเคชัน Java ที่ทำงานบนเว็บเซิร์ฟเวอร์เรียกว่าเป็น servlet Servlets เลียนแบบเว็บแอปพลิเคชัน พวกเขาวิ่งเข้าไปในภาชนะ Apache Tomcat เป็นตัวอย่างยอดนิยมของภาชนะบรรจุดังกล่าว ซอฟต์แวร์คอนเทนเนอร์แปลคำขอ HTTP RAW และการตอบกลับเป็นวัตถุ Java และจัดเตรียมไว้สำหรับ Servlets เว็บไซต์คงที่ให้บริการเนื้อหาเดียวกันสำหรับทุกคำขอ HTTP แต่ servlet สามารถสร้างเนื้อหาแบบไดนามิกที่แตกต่างกันสำหรับคำขอ HTTP ทุกครั้งที่ทำ
แอป TODO Web ที่เรากำลังสร้างจะมีหลายส่วน - servlets, ตัวกรอง, ไฟล์ JSP, คลาสฐานข้อมูล, POJOS ฯลฯ เราจะรวมเข้าด้วยกันทั้งหมด (เช่นการรวมเข้าด้วยกัน) ภายใต้บริบทแอปพลิเคชันเดียว (หรือสภาพแวดล้อมแอปพลิเคชัน) บนคอนเทนเนอร์ (TOMCAT) เราจะเรียกบริบทของแอปพลิเคชันนี้ว่า 'TODO' ดังนั้นหากเราใช้ Tomcat บนพีซีของเราที่พอร์ต 8080 บริบทแอปพลิเคชันของเราสามารถเข้าถึงได้ผ่าน URL:
http://localhost:8080/todo/
เลเยอร์ข้อมูลของเราประกอบด้วยการใช้รูปแบบ DAO และรูปแบบโรงงานที่ด้านบนของแหล่งข้อมูล JDBC เราเลือก DataSource JDBC แทน DriverManager เพราะเราต้องการเก็บเกี่ยวผลประโยชน์ของการรวมการเชื่อมต่อ
เราเพิ่งมีเซิร์ฟเล็ตหนึ่งตัวที่ทำหน้าที่เป็น คอนโทรลเลอร์ ที่เรียกว่า 'หลัก' คำขอ HTTP ของผู้ใช้เป็นการดำเนินการสำหรับเรา ดังนั้นวัตถุประสงค์ของ Servlet คอนโทรลเลอร์ของเราคือเพียงแค่เลือกการดำเนินการที่เหมาะสมสำหรับคำขอ HTTP Servlet คอนโทรลเลอร์เลือกตัวจัดการการกระทำและส่งมอบให้กับคำขอที่ทำโดยผู้ใช้ เราไม่เขียนขั้นตอนการดำเนินการในคอนโทรลเลอร์ของเรา เราทำให้มันสะอาดและเอนตัว จุดประสงค์ของมันคือ 'เลือก' ตัวจัดการแอ็คชั่น ไม่ต้อง 'ดำเนินการ' การกระทำด้วยตัวเอง หลังจากการดำเนินการจัดการดำเนินการดำเนินการที่ร้องขอคอนโทรลเลอร์จะได้รับ 'ขั้นตอนต่อไป' เพื่อดำเนินการเพื่อตอบสนองจากตัวจัดการการกระทำ งานของคอนโทรลเลอร์คือการเลือกทรัพยากรที่ดำเนินการตอบกลับที่ร้องขอ โดยสรุปเราทำให้คอนโทรลเลอร์ของเราอยู่ห่างจากตรรกะทางธุรกิจทั้งหมด
เราจะแมป servlet คอนโทรลเลอร์ของเรากับรูปแบบ URI /app/* ดังนั้น servlet คอนโทรลเลอร์ของเราจะจัดการทุก URI ที่ติดตามรูปแบบ /app/
การดำเนินการที่ผู้ใช้ร้องขอคือตรรกะทางธุรกิจสำหรับแอปพลิเคชันของเรา แอ็คชั่นตัวจัดการเป็น แบบจำลอง ในการใช้งาน MVC ของเรา การกระทำทุกครั้งจะต้องใช้อินเทอร์เฟซการดำเนินการ:
public interface Action {
/*
An action is supposed to execute and return results. ActionResponse represents the response.
*/
public abstract ActionResponse execute ( HttpServletRequest request , HttpServletResponse response )
throws Exception ;
}การกระทำควรจะดำเนินการและส่งคืนผลลัพธ์ เราสร้างคลาสพิเศษสำหรับผลลัพธ์ดังกล่าว - ActionResponse ตัวจัดการการกระทำสามารถเลือกที่จะ 'ส่งต่อ' หรือ 'เปลี่ยนเส้นทาง'
public class ActionResponse {
private String method ;
private String viewPath ;
public ActionResponse () {
this . method = "" ;
this . viewPath = "" ;
}
public void setMethod ( String method ) {
this . method = method ;
}
public String getMethod () {
return this . method ;
}
public void setViewPath ( String viewPath ) {
this . viewPath = viewPath ;
}
public String getViewPath () {
return this . viewPath ;
}
@ Override
public String toString () {
return this . getClass (). getName ()+ "[" + this . method + ":" + this . viewPath + "]" ;
}
}เราใช้รูปแบบการออกแบบโรงงาน เราสร้างคลาสโรงงาน - ActionFactory - เพื่อให้คลาส Handler Action เราต้องการ:
public class ActionFactory {
private static Map < String , Action > actions = new HashMap < String , Action >() {
{
put ( new String ( "POST/login" ), new LoginAction ());
put ( new String ( "GET/login" ), new LoginAction ());
put ( new String ( "GET/logout" ), new LogoutAction ());
put ( new String ( "GET/admin/accounts/dashboard" ), new AdminAccountsDashboardAction ());
put ( new String ( "GET/admin/accounts/new" ), new AdminNewAccountFormAction ());
put ( new String ( "POST/admin/accounts/create" ), new AdminCreateAccountAction ());
put ( new String ( "GET/admin/accounts/details" ), new AdminReadAccountDetailsAction ());
put ( new String ( "POST/admin/accounts/update" ), new AdminUpdateAccountAction ());
put ( new String ( "GET/tasks/dashboard" ), new UserTasksDashboardAction ());
put ( new String ( "GET/tasks/new" ), new UserNewTaskFormAction ());
put ( new String ( "GET/tasks/details" ), new UserReadTaskDetailsAction ());
put ( new String ( "POST/tasks/create" ), new UserCreateTaskAction ());
put ( new String ( "POST/tasks/update" ), new UserUpdateTaskAction ());
put ( new String ( "GET/users/profile" ), new UserReadProfileAction ());
put ( new String ( "POST/users/update" ), new UserUpdateProfileAction ());
}
;
};
public static Action getAction ( HttpServletRequest request ) {
Action action = actions . get ( request . getMethod () + request . getPathInfo ());
if ( action == null ) {
return new UnknownAction ();
} else {
return action ;
}
}
}วัตถุประสงค์ของ servlet คอนโทรลเลอร์ของเราคือ:
protected void processRequest ( HttpServletRequest request , HttpServletResponse response )
throws ServletException , IOException {
Action action = ActionFactory . getAction ( request );
try {
ActionResponse actionResponse = action . execute ( request , response );
if ( actionResponse . getMethod (). equalsIgnoreCase ( "forward" )) {
System . out . println ( this . getClass (). getCanonicalName () + ":forward:" + actionResponse );
this . getServletContext (). getRequestDispatcher ( actionResponse . getViewPath ()). forward ( request , response );
} else if ( actionResponse . getMethod (). equalsIgnoreCase ( "redirect" )) {
System . out . println ( this . getClass (). getCanonicalName () + ":redirect:" + actionResponse );
if ( actionResponse . getViewPath (). equals ( request . getContextPath ())) {
response . setHeader ( "Cache-Control" , "no-cache, no-store, must-revalidate" );
response . setHeader ( "Pragma" , "no-cache" );
response . setDateHeader ( "Expires" , 0 );
}
response . sendRedirect ( actionResponse . getViewPath ());
} else if ( actionResponse . getMethod (). equalsIgnoreCase ( "error" )) {
System . out . println ( this . getClass (). getCanonicalName () + ":error:" + actionResponse );
response . sendError ( 401 );
} else {
System . out . println ( this . getClass (). getCanonicalName () + ":" + actionResponse );
response . sendRedirect ( request . getContextPath ());
}
} catch ( Exception e ) {
e . printStackTrace ();
}
} ตารางด้านล่างแสดงรายการของคำขอ HTTP แอปพลิเคชันของเราตอบสนองและตัวจัดการการดำเนินการที่เกี่ยวข้อง:
| การกระทำที่ผู้ใช้ตั้งใจ | http ขอ URI | ผู้ดำเนินการ |
|---|---|---|
| ส่งข้อมูลรับรองการเข้าสู่ระบบที่ว่างเปล่า | GET /app/login | การเข้าสู่ระบบ |
| ส่งข้อมูลรับรองเข้าสู่ระบบ | POST /app/login | การเข้าสู่ระบบ |
| รับแดชบอร์ดบัญชี | GET /app/admin/accounts/dashboard | adminaccountsdashboardAction |
| รับแบบฟอร์มบัญชีใหม่ | GET /app/admin/accounts/new | adminNewaccountFormAction |
| ส่งรายละเอียดบัญชีใหม่ | POST /app/admin/accounts/create | AdminCreateAccountAction |
| รับรายละเอียดของบัญชี | GET /app/admin/accounts/details?id=xx | AdminReadAccountDetailSaction |
| อัปเดตรายละเอียดของบัญชี | POST /app/admin/accounts/update | AdminUpDateAccountAction |
| รับงานแดชบอร์ด | GET /app/tasks/dashboard | usertasksdashboardaction |
| รับแบบฟอร์มงานใหม่ | GET /app/tasks/new | UserNewTaskFormAction |
| ส่งรายละเอียดงานใหม่ | POST /app/tasks/create | userCreateTaskAction |
| รับรายละเอียดของงาน | GET /app/tasks/details?id=xx | userReadTaskDetailSaction |
| อัปเดตรายละเอียดของงาน | POST /app/tasks/update | userUpDatASkAction |
| รับรายละเอียดโปรไฟล์ของฉัน | GET /app/users/profile | userReadProfileAction |
| อัปเดตรายละเอียดโปรไฟล์ของฉัน | POST /app/users/update | userUpdateProfileAction |
| ออกจากระบบ | GET /app/logout | การออกจากระบบ |
งานของตัวจัดการการกระทำคือการดำเนินการตรรกะทางธุรกิจและเลือกส่วนประกอบมุม มอง ที่เหมาะสมเป็นการตอบสนองต่อคำขอที่ทำโดยผู้ใช้ ตารางด้านล่างแสดงตัวจัดการการกระทำทั้งหมดและส่วนประกอบมุมมองของพวกเขา:
| ผู้ดำเนินการ | ดูส่วนประกอบ |
|---|---|
| การเข้าสู่ระบบ | /WEB-INF/pages/admin/accounts/dashboard.jsp/WEB-INF/pages/tasks/dashboard.jsp |
| adminaccountsdashboardAction | /WEB-INF/pages/admin/accounts/dashboard.jsp |
| adminNewaccountFormAction | /WEB-INF/pages/admin/accounts/newAccount.jsp |
| AdminCreateAccountAction | /WEB-INF/pages/admin/accounts/createAccountResult.jsp |
| AdminReadAccountDetailSaction | /WEB-INF/pages/admin/accounts/accountDetails.jsp |
| AdminUpDateAccountAction | /WEB-INF/pages/admin/accounts/updateAccountResult.jsp |
| usertasksdashboardaction | /WEB-INF/pages/tasks/dashboard.jsp |
| UserNewTaskFormAction | /WEB-INF/pages/tasks/newTask.jsp |
| userCreateTaskAction | /WEB-INF/pages/tasks/createTaskResult.jsp |
| userReadTaskDetailSaction | /WEB-INF/pages/tasks/taskDetails.jsp |
| userUpDatASkAction | /WEB-INF/pages/tasks/updateTaskResult.jsp |
| userReadProfileAction | /WEB-INF/pages/users/viewProfile.jsp |
| userUpdateProfileAction | /WEB-INF/pages/users/updateProfileResult.jsp |
| ไม่รู้จัก | /WEB-INF/pages/users/unknownAction.jsp |
ส่วนประกอบมุมมองสร้างการตอบสนอง HTML ที่ต้องการซึ่งจะถูกส่งไปยังผู้ใช้ ดูส่วนประกอบอ่านข้อความที่กำหนดโดย Action Handler และแสดงให้ผู้ใช้
เราใช้ตัวกรองเพื่อสกัดกั้นการร้องขอ HTTP ที่เข้ามา ตัวกรองทั้งหมดจะถูกใช้ก่อนที่คำขอจะถูกส่งผ่านไปยัง Servlet คอนโทรลเลอร์ คำขอ HTTP ที่เข้ามาใด ๆ จะได้รับการจัดการครั้งแรกโดยตัวกรองการรับรองความถูกต้อง ผ่านตัวกรองนี้เราตรวจสอบว่าผู้ใช้เข้าสู่ระบบแล้วหรือไม่ หากไม่ได้ลงชื่อเข้าใช้เราจะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าเข้าสู่ระบบ หลังจากผ่านตัวกรองการรับรองความถูกต้องแล้วคำขอ HTTP จะถูกดักจับด้วยตัวกรองอีกสองตัว ในตัวกรองเหล่านี้เราตรวจสอบเส้นทาง URI และผู้ใช้คือ 'ผู้ดูแลระบบ' หรือผู้ใช้ปกติ หากผู้ใช้ปกติพยายามเข้าถึงเส้นทาง URI 'Admin' เราจะป้องกันการเข้าถึงดังกล่าว หากผู้ใช้ 'Admin' กำลังพยายามเข้าถึงงาน URI ที่เกี่ยวข้องเราจะป้องกันการเข้าถึงดังกล่าว
เฉพาะ 'ผู้ดูแลระบบ' เท่านั้นที่สามารถเข้าถึง URIs ที่ขึ้นต้นด้วย /app/admin/* และผู้ใช้ปกติเท่านั้นที่สามารถเข้าถึง URIS ที่ขึ้นต้นด้วย /app/tasks/* URIS /app/login อื่น ๆ /app/logout /app/users/* สามารถเข้าถึงได้โดยทั้งคู่
เราไม่สกัดกั้นการตอบสนองที่เราส่งออกไป
ดังนั้นนี่คือลักษณะของแอปพลิเคชันของเรา ฉันเก็บ UI ธรรมดาไว้เพื่อความเรียบง่าย ไม่มี CSS หรือ JavaScript










