Python email.headerregistry: อ่านหัวอีเมลแบบมีชนิด ไม่ต้องตัดสตริงเอง
หัวอีเมลในโลกจริงมีชื่อคน ที่อยู่ และพารามิเตอร์ปะปนกันครับ การตัดสตริงด้วยมือพังง่ายเมื่อมี quote หรือ charset email.headerregistry ให้ชนิดข้อมูลที่แยกชื่อกับที่อยู่ และอ่าน Content-Type เป็น typed header
WHY: อย่าให้สตริงดิบเป็นสัญญา
เหมือนซองจดหมายที่เขียนชื่อกับเลขบ้านคนละช่อง การเก็บทั้งก้อนเป็นข้อความเดียวทำให้ parser คนละทีมตีความต่างกัน Typed header ลดจุดที่ต้องเดาว่าเครื่องหมายจุลภาคอยู่ในชื่อหรือคั่นรายการ
HOW: อ่าน Address และ Content-Type
import unittest
from email import message_from_string
from email.policy import default
class HeaderRegistryTests(unittest.TestCase):
def test_from_address_parts(self) -> None:
raw = "From: Showkhun <[email protected]>\n\nbody\n"
msg = message_from_string(raw, policy=default)
addr = msg["From"].addresses[0]
self.assertEqual(addr.display_name, "Showkhun")
self.assertEqual(addr.addr_spec, "[email protected]")
def test_content_type_params(self) -> None:
raw = (
"Content-Type: text/plain; charset=utf-8; format=flowed\n"
"\nhello\n"
)
msg = message_from_string(raw, policy=default)
ct = msg["Content-Type"]
self.assertEqual(ct.content_type, "text/plain")
self.assertEqual(ct.params["charset"], "utf-8")
self.assertEqual(ct.params["format"], "flowed")
if __name__ == "__main__":
unittest.main()
ใช้ policy=default เพื่อให้ HeaderRegistry ทำงาน การอ่านด้วย policy เก่าอาจได้สตริงดิบแทน typed object
ขอบเขตที่ยังต้องระวัง
ข้อความจากระบบเก่าอาจมี header ผิดรูป ใส่ try/except รอบการอ่านฟิลด์สำคัญ และอย่าเชื่อ display_name ในการยืนยันตัวตน — ยืนยันที่ addr_spec หรือลายเซ็นแทน
Recap
- เปิด message ด้วย modern policy เพื่อได้ typed headers
- แยก
display_nameกับaddr_specแทนการตัดสตริง - อ่านพารามิเตอร์ Content-Type จาก
.params - อย่าใช้ชื่อแสดงผลเป็นหลักฐานยืนยันตัวตน