microdot --- เฟรมเวิร์ก HTTP ขนาดเล็ก

Microdot เป็นเฟรมเวิร์ก HTTP ขนาดเล็กที่ได้รับแรงบันดาลใจจาก Flask สำหรับ MicroPython ทำงานบนพื้นฐานของ asyncio รองรับหลายไคลเอนต์พร้อมกันโดยไม่ต้องใช้เธรด และเปิดเผย API แบบ route-decorator ที่คุ้นเคย ตัวอย่างแอปพลิเคชันขั้นต่ำมีลักษณะดังนี้:

from microdot import Microdot

app = Microdot()

@app.route('/')
async def index(request):
    return 'Hello, world!'

app.run(host='0.0.0.0', port=80)

class Microdot

class microdot.Microdot

อินสแตนซ์แอปพลิเคชัน HTTP สร้างอินสแตนซ์หนึ่งไว้ใกล้ส่วนบนของสคริปต์และตกแต่งด้วย route handler แล้วเรียก run() หรือรอ start_server() เพื่อเริ่มให้บริการ

การลงทะเบียน Route

route(url_pattern: str, methods: list | None = None) Callable

Decorator ที่ลงทะเบียน handler สำหรับ url_pattern ภายใต้ HTTP method ที่ระบุ (ค่าเริ่มต้น ['GET']) คืนค่า decorator ซึ่งเมื่อนำไปใช้กับฟังก์ชันจะลงทะเบียนเป็น handler และคืนฟังก์ชันนั้นโดยไม่เปลี่ยนแปลง

url_pattern

รูปแบบ path รองรับส่วนคงที่ (/users) และส่วนไดนามิกที่ล้อมรอบด้วย < / > ส่วนไดนามิกยอมรับคำนำหน้าประเภทที่คั่นด้วย : -- <int:id>, <path:rest>, <re:[0-9a-f]+:hex> ประเภทเริ่มต้นคือ string

methods

รายการชื่อ HTTP method หากละไว้จะจับคู่เฉพาะ GET เท่านั้น

Handler ถูกเรียกด้วย request object เป็นอาร์กิวเมนต์แรก ตามด้วยส่วนไดนามิกที่ดักจับได้เป็น keyword argument ค่าที่ handler คืนกลับมาจะกลายเป็น HTTP response ได้แก่ Response, string, tuple (body, status_code[, headers]), หรือ dict / list (ส่งเป็น JSON)

get(url_pattern: str) Callable

ชื่อแทนสำหรับ route(url_pattern, methods=['GET']) -- ลงทะเบียนฟังก์ชันที่ตกแต่งเป็น GET handler สำหรับ url_pattern คืนค่า decorator

post(url_pattern: str) Callable

ชื่อแทนสำหรับ route(url_pattern, methods=['POST']) -- ลงทะเบียนฟังก์ชันที่ตกแต่งเป็น POST handler สำหรับ url_pattern คืนค่า decorator

put(url_pattern: str) Callable

ชื่อแทนสำหรับ route(url_pattern, methods=['PUT']) -- ลงทะเบียนฟังก์ชันที่ตกแต่งเป็น PUT handler สำหรับ url_pattern คืนค่า decorator

patch(url_pattern: str) Callable

ชื่อแทนสำหรับ route(url_pattern, methods=['PATCH']) -- ลงทะเบียนฟังก์ชันที่ตกแต่งเป็น PATCH handler สำหรับ url_pattern คืนค่า decorator

delete(url_pattern: str) Callable

ชื่อแทนสำหรับ route(url_pattern, methods=['DELETE']) -- ลงทะเบียนฟังก์ชันที่ตกแต่งเป็น DELETE handler สำหรับ url_pattern คืนค่า decorator

Lifecycle hooks

before_request(f: Callable) Callable

Decorator ที่ลงทะเบียน f ให้ทำงานก่อนทุก request f รับ request object และค่าที่คืนมักถูกละเว้น หากต้องการหยุดการประมวลผล request ให้คืนค่า Response (หรือค่าที่แปลงเป็นหนึ่งได้) -- pipeline ที่เหลือจะถูกข้ามและส่ง response นั้น คืนค่า f โดยไม่เปลี่ยนแปลง

after_request(f: Callable) Callable

Decorator ที่ลงทะเบียน f ให้ทำงานหลัง request ที่สำเร็จทุกรายการ f รับ (request, response) และต้องคืน response object (ที่อาจปรับแล้ว) คืนค่า f โดยไม่เปลี่ยนแปลง

after_error_request(f: Callable) Callable

Decorator ที่ลงทะเบียน f ให้ทำงานหลังจาก Microdot สร้าง error response (404, 500, exception ที่เกิดขึ้น ฯลฯ) f รับ (request, response) และต้องคืน response object (ที่อาจปรับแล้ว) คืนค่า f โดยไม่เปลี่ยนแปลง

errorhandler(status_code_or_exception_class) Callable

Decorator ที่ลงทะเบียน handler กำหนดเองสำหรับ HTTP status code หรือ Python exception class สำหรับ status code handler รับเฉพาะ request เท่านั้น สำหรับ exception class รับ (request, exception) คืนค่า decorator

การ Mount และการยกเลิก

mount(subapp: Microdot, url_prefix: str = '', local: bool = False) None

แนบ route ของอินสแตนซ์ Microdot อื่นภายใต้ url_prefix เมื่อ local เป็น False (ค่าเริ่มต้น) before / after / error handler ของ sub-app จะถูกแนบกับ parent ด้วย เมื่อเป็น True handler เหล่านั้นจะทำงานเฉพาะสำหรับ sub-app route เท่านั้น คืนค่า None

static abort(status_code: int, reason: str | None = None) None

เรียก HTTPException เพื่อยกเลิก request ปัจจุบันด้วย status code ที่กำหนด ไม่มีการคืนค่าปกติ -- เฟรมเวิร์กจะดักจับ exception และแปลงเป็น error response ที่สอดคล้อง ใช้งานได้สะดวกภายใน route body:

from microdot import abort

@app.get('/users/<int:id>')
def get_user(request, id):
    user = lookup(id)
    if user is None:
        abort(404)
    return user.to_dict()

การรันเซิร์ฟเวอร์

run(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None) None

บล็อก thread ที่เรียก รัน asyncio.run() บน start_server() คืนค่า None เฉพาะหลังจาก shutdown() ถูกเรียกและ listening loop ออก วิธีที่ง่ายที่สุดในการเปิดแอปแบบ standalone:

app.run(host='0.0.0.0', port=80)
async start_server(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None, start_serving: bool = True) None

เริ่มเซิร์ฟเวอร์เป็น coroutine ใช้สิ่งนี้เมื่อรวมกับ asyncio event loop ที่มีอยู่พร้อมกับ task อื่น coroutine จะไม่คืนค่าจนกว่า shutdown() จะถูกเรียก:

async def main():
    await asyncio.gather(
        app.start_server(port=80),
        capture_loop(),
    )
host

Network interface ที่จะรับฟัง '0.0.0.0' (ค่าเริ่มต้น) หมายถึงทุก interface; '127.0.0.1' หมายถึง loopback เท่านั้น

port

TCP port ที่จะรับฟัง ค่าเริ่มต้น 5000 -- เลือก 80 สำหรับ HTTP ธรรมดา, 443 สำหรับ HTTPS

debug

หากเป็น True จะบันทึกทุก request และแสดง traceback ไปยัง stdout

ssl

ssl.SSLContext สำหรับห่อการเชื่อมต่อขาเข้าใน TLS None (ค่าเริ่มต้น) หมายถึง HTTP ธรรมดา

start_serving

ใช้ได้เฉพาะบน CPython เท่านั้น ถูกละเว้นบน MicroPython

shutdown() None

ขอปิดเซิร์ฟเวอร์อย่างนุ่มนวล ปลอดภัยที่จะเรียกจาก route handler -- request ปัจจุบันเสร็จสิ้นก่อนที่ loop จะออก คืนค่าทันที การปิดเซิร์ฟเวอร์จริงจะเกิดขึ้นเมื่อ request ที่กำลังดำเนินการเสร็จสิ้น

Attribute

url_map: list

รายการ route ที่ลงทะเบียนแล้วเป็น tuple (methods, URLPattern, handler, url_prefix, subapp)

before_request_handlers: list

รายการ callable ที่ลงทะเบียนด้วย before_request() ตามลำดับการลงทะเบียน แต่ละรายการทำงานกับ request object ก่อน route handler การปรับเปลี่ยน list โดยตรงใช้งานได้แต่แทบไม่จำเป็น -- แนะนำให้ใช้ decorator

after_request_handlers: list

รายการ callable ที่ลงทะเบียนด้วย after_request() ตามลำดับการลงทะเบียน แต่ละรายการทำงานกับ (request, response) หลัง request ที่สำเร็จและต้องคืน response (ที่อาจปรับแล้ว)

after_error_request_handlers: list

รายการ callable ที่ลงทะเบียนด้วย after_error_request() ตามลำดับการลงทะเบียน แต่ละรายการทำงานกับ (request, response) หลังจาก error response ถูกสร้าง (โดยเฟรมเวิร์กหรือ application error handler) และต้องคืน response

error_handlers: dict

Mapping ของ error key ไปยัง handler callable ที่เติมข้อมูลโดย errorhandler() key เป็น HTTP status code (int) หรือ Python exception class; value คือ handler ที่ลงทะเบียน Status-code handler รับ (request); exception-class handler รับ (request, exception)

debug: bool

True ขณะที่เซิร์ฟเวอร์กำลังทำงานด้วย debug=True

class Request

class microdot.Request

HTTP request ขาเข้า อินสแตนซ์ถูกส่งไปยัง route handler เป็นอาร์กิวเมนต์ตำแหน่งแรก แอปพลิเคชันไม่สร้าง Request โดยตรง

Class attribute

max_content_length: int

ปฏิเสธ request ที่มี Content-Length เกินจำนวนไบต์นี้ด้วย response 413 ค่าเริ่มต้น 16 KB

max_body_length: int

ขนาด body ที่ใหญ่ที่สุดที่บัฟเฟอร์ไว้ในหน่วยความจำและเปิดเผยผ่าน body body ที่ใหญ่กว่า (ไม่เกิน max_content_length) จะอยู่บน socket และต้องอ่านผ่าน stream ค่าเริ่มต้น 16 KB

max_readline: int

ความยาวสูงสุดของ request line หรือ header line หนึ่งบรรทัดในหน่วยไบต์ ค่าเริ่มต้น 2 KB

Instance attribute

app: Microdot

อินสแตนซ์ Microdot ที่จัดการ request

client_addr: tuple

ที่อยู่ของ client เป็น (host, port)

method: str

HTTP method string ('GET', 'POST', ...)

scheme: str

'http' หรือ 'https'

url: str

URL path และ query string ของ request แบบเต็ม (ทุกอย่างหลัง host)

path: str

เฉพาะส่วน path

query_string: str | None

ส่วน query string ดิบ หรือ None

args: MultiDict

Query string ที่แปลงแล้วเป็น MultiDict

headers: NoCaseDict

Request header เป็น NoCaseDict (การค้นหาแบบไม่คำนึงถึงตัวพิมพ์)

cookies: dict

Cookie header ที่แปลงแล้วเป็น dict

content_length: int

ค่า Content-Length แบบ integer หรือ 0 หากไม่มี

content_type: str | None

ค่า Content-Type header หรือ None

g: object

คอนเทนเนอร์แบบอิสระต่อ request (object ว่างเปล่า) กำหนด attribute เพื่อส่งค่าระหว่าง hook และ handler (request.g.user = ...)

route: Callable

ฟังก์ชัน handler ที่จับคู่กับ request นี้

url_prefix: str

Prefix ที่ route ถูก mount ไว้ หรือ ''

subapp: Microdot | None

อินสแตนซ์ sub-app ที่ mount ไว้ หรือ None

การเข้าถึง Body

body: bytes

Request body แบบเต็มเป็น bytes ว่างเปล่าเมื่อ body กำลังถูก stream (ดู stream)

stream: object

async stream object ที่เปิดเผย read() บน body ใช้สิ่งนี้สำหรับ body ที่ใหญ่กว่า max_body_length

json: dict | list | str | int | float | bool | None

Body ที่แปลงเป็น JSON (dict, list, str, int, float, หรือ bool -- ตามที่ payload เข้ารหัส) หรือ None หาก Content-Type ไม่ใช่ application/json

form: MultiDict | None

Form field ที่เข้ารหัส URL เป็น MultiDict หรือ None สำหรับ multipart/form-data ให้ตกแต่ง route ด้วย microdot.multipart.with_form_data()

files: dict | None

ไฟล์ที่อัปโหลดเป็น {name: FileUpload} ถูกเติมข้อมูลโดย microdot.multipart.with_form_data() None จนกว่า decorator นั้นจะทำงาน

after_request(f: Callable) Callable

ลงทะเบียน after-request hook เฉพาะ request -- ทำงานหลัง application-level after-request handler เฉพาะเมื่อสำเร็จ f รับ (request, response) และต้องคืน response object (ที่อาจปรับแล้ว) คืนค่า f โดยไม่เปลี่ยนแปลง

class Response

class microdot.Response(body=b'', status_code: int = 200, headers: dict | None = None, reason: str | None = None)

HTTP response ส่วนใหญ่ handler คืนค่าที่ Microdot แปลงเป็น Response โดยอัตโนมัติ สร้างโดยตรงเมื่อต้องการ header กำหนดเองหรือ status code เฉพาะ

body

Response body str เข้ารหัสเป็น UTF-8; dict / list เข้ารหัสเป็น JSON และตั้ง Content-Type ตามนั้น; bytes ส่งตรง; file-like object หรือ async generator จะ stream

status_code

HTTP status แบบตัวเลข ค่าเริ่มต้น 200

headers

Dict ของ response header (ไม่คำนึงถึงตัวพิมพ์)

reason

วลีเหตุผลกำหนดเอง ค่าเริ่มต้นคือ "OK" สำหรับ 200 และ "N/A" สำหรับอื่น ๆ

Class attribute

default_content_type: str

Content-Type ที่ใช้เมื่อไม่ได้ตั้งค่าอย่างชัดเจน ค่าเริ่มต้น 'text/plain'

default_send_file_max_age: int | None

ค่า Cache-Control: max-age สำหรับ send_file() เมื่อไม่ได้กำหนด max_age None (ค่าเริ่มต้น) หมายถึงไม่มี Cache-Control header

send_file_buffer_size: int

ขนาด chunk สำหรับ streaming ของ send_file() ค่าเริ่มต้น 1024

already_handled: None

Sentinel ที่คืนจาก handler ที่เขียน response โดยตรงแล้ว (ใช้โดย WebSocket upgrade) เสมอ None; สิ่งสำคัญคือ identity

types_map: dict

Map ของ extension ไปยัง mime-type ที่ใช้โดย send_file() สำหรับการอนุมาน content-type จับคู่ css, gif, html, jpg, js, json, png, txt, svg

Method

เพิ่ม Set-Cookie header expires อาจเป็น string ที่จัดรูปแบบล่วงหน้าหรือ object คล้าย datetime ที่มี timetuple() คืนค่า None; header ถูกต่อท้าย response นี้ในที่เดิม

ตั้ง Set-Cookie ที่ทำให้ cookie ที่กำหนดหมดอายุทันที kwargs ยอมรับตัวเลือกเดียวกับ set_cookie() (path, domain, secure, http_only, partitioned); expires และ max_age ถูกละเว้น คืนค่า None; header ถูกต่อท้าย response นี้ในที่เดิม

classmethod redirect(location: str, status_code: int = 302) Response

คืน redirect response:

@app.get('/old')
def old(request):
    return Response.redirect('/new')
classmethod send_file(filename: str, status_code: int = 200, content_type: str | None = None, stream=None, max_age: int | None = None, compressed: bool | str = False, file_extension: str = '') Response

Stream ไฟล์จาก filesystem เป็น response body content_type อนุมานจาก extension ผ่าน types_map หากไม่ระบุ compressed=True ตั้ง Content-Encoding: gzip (ไฟล์ต้องถูกบีบอัดไว้แล้ว)

Warning

filename ถูกเปิดโดยตรง ห้ามส่ง path ที่ผู้ใช้กำหนดโดยไม่ผ่านการตรวจสอบ -- การทำเช่นนั้นทำให้เปิดเผยไฟล์ตามอำเภอใจได้

Exception

exception microdot.HTTPException

เรียกโดย abort() เพื่อหยุด request ด้วย status code เฉพาะ Microdot ดักจับสิ่งนี้และแปลงเป็น error response ที่สอดคล้อง

status_code: int

HTTP status code แบบตัวเลขที่จะคืนกลับ -- ค่าที่ส่งไปยัง abort()

reason: str

วลีเหตุผลที่จะรวมใน error response หากไม่ได้กำหนดให้ abort() ค่าเริ่มต้นคือ "<status_code> error" (เช่น "404 error")

class URLPattern

class microdot.URLPattern(url_pattern: str)

รูปแบบที่คอมไพล์แล้วของ URL pattern ของ route สร้างโดยอัตโนมัติโดย Microdot.route(); แอปพลิเคชันแทบไม่สร้างอินสแตนซ์โดยตรง

classmethod register_type(type_name: str, pattern: str = '[^/]+', parser: Callable | None = None) None

ลงทะเบียนประเภทส่วนไดนามิกใหม่สำหรับใช้ใน route pattern คืนค่า None; ประเภทถูกเพิ่มในรีจิสทรีระดับ class ตัวอย่างเช่น การเพิ่มประเภท <uuid:...>

URLPattern.register_type('uuid', '[0-9a-f-]{36}')

parser คือ callable ที่ไม่บังคับซึ่งแปลง string ที่ดักจับได้ก่อนที่จะถึง handler

match(path: str) dict | None

จับคู่ path กับ pattern และคืน dict ของกลุ่มที่ดักจับได้ หรือ None หากไม่มีการจับคู่

Helper class

class microdot.MultiDict(initial_dict: dict | None = None)

dict subclass ที่เก็บค่าหลายค่าต่อ key ใช้สำหรับ query string และ URL-encoded form body ที่ key เดียวกันอาจปรากฏมากกว่าหนึ่งครั้ง (?tag=a&tag=b)

get(key, default=None, type: Callable | None = None)

คืนค่าแรกสำหรับ key พร้อมแปลงด้วย type (callable) หากต้องการ คืน default หาก key ไม่มีหรือการแปลงล้มเหลว มิฉะนั้นคืนค่า (ที่แปลงแล้วหากมี)

getlist(key, type: Callable | None = None) list

คืนค่าทั้งหมดสำหรับ key เป็น list พร้อมแปลงค่าแต่ละค่าด้วย type หากต้องการ คืน list ว่างหาก key ไม่มีอยู่

class microdot.NoCaseDict

dict subclass ที่มี key string แบบไม่คำนึงถึงตัวพิมพ์ ใช้สำหรับ Request.headers และ Response.headers

ฟังก์ชันระดับ module

microdot.abort(status_code: int, reason: str | None = None) None

ชื่อแทนสำหรับ Microdot.abort() ไม่มีการคืนค่าปกติ -- เรียก HTTPException สามารถ import ได้เป็น from microdot import abort

microdot.redirect(location: str, status_code: int = 302) Response

ชื่อแทนสำหรับ Response.redirect() สามารถ import ได้เป็น from microdot import redirect

microdot.send_file(filename: str, **kwargs) Response

ชื่อแทนสำหรับ Response.send_file()

microdot.urlencode(s: str) str

เข้ารหัส URL component แบบ percent สลับตัวอักษรที่มีความหมายพิเศษใน URL (/, ?, &, =, #, space, ...) ด้วย hex escape %xx เพื่อให้ผลลัพธ์วางภายใน path segment หรือ query value ได้อย่างปลอดภัย คืนค่า str ที่เข้ารหัสแล้ว

microdot.urldecode(s: str) str

ถอดรหัส URL component แบบ percent -- ตรงข้ามกับ urlencode() %xx escape ถูกแทนที่ด้วยไบต์ที่เข้ารหัส และ + แปลงเป็น space (ธรรมเนียม query-string ในอดีต) คืนค่า str ที่ถอดรหัสแล้ว

Submodule