นากกายกำลังติดป้ายชื่อและที่อยู่แยกช่องบนซองจดหมายมิ้นต์ ไม่ตัดสตริงด้วยมือ

Python email.headerregistry: อ่านหัวอีเมลแบบมีชนิด ไม่ต้องตัดสตริงเอง

น้องกายEngineering

หัวอีเมลในโลกจริงมีชื่อคน ที่อยู่ และพารามิเตอร์ปะปนกันครับ การตัดสตริงด้วยมือพังง่ายเมื่อมี 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
  • อย่าใช้ชื่อแสดงผลเป็นหลักฐานยืนยันตัวตน