litefs.handlers.response 源代码

#!/usr/bin/env python
# coding: utf-8

"""
HTTP 响应对象和响应构建工具

提供 Response 类和相关的响应构建方法
"""

import json
import os
import sys
from http.client import responses as http_status_codes

from .._version import __version__

# 默认配置
default_content_type = "text/html; charset=utf-8"
json_content_type = "application/json; charset=utf-8"

# 服务器信息
server_info = "litefs/%s python/%s" % (__version__, sys.version.split()[0])

# 默认错误页面模板
DEFAULT_STATUS_MESSAGE = """\
<html>
    <head>
        <meta charset="utf-8">
        <title>HTTP response</title>
    </head>
    <body>
        <h1>HTTP response</h1>
        <p>HTTP status %(code)d.
        <p>Message: %(message)s.
        <p>HTTP code explanation: %(code)s = %(explain)s.
    </body>
</html>"""


[文档] class Response: """ 响应对象,提供更丰富的响应方法 """ def __init__(self, content=None, status_code=200, headers=None): self.content = content self.status_code = status_code self.headers = headers or []
[文档] @classmethod def json(cls, data, status_code=200, headers=None): """ 返回 JSON 响应 """ content = json.dumps(data, ensure_ascii=False) headers = headers or [] # 确保设置正确的 Content-Type has_content_type = any(h[0].lower() == 'content-type' for h in headers) if not has_content_type: headers.insert(0, ("Content-Type", "application/json; charset=utf-8")) return cls(content, status_code, headers)
[文档] @classmethod def html(cls, content, status_code=200, headers=None): """ 返回 HTML 响应 """ headers = headers or [] # 确保设置正确的 Content-Type has_content_type = any(h[0].lower() == 'content-type' for h in headers) if not has_content_type: headers.insert(0, ("Content-Type", "text/html; charset=utf-8")) return cls(content, status_code, headers)
[文档] @classmethod def text(cls, content, status_code=200, headers=None): """ 返回纯文本响应 """ headers = headers or [] # 确保设置正确的 Content-Type has_content_type = any(h[0].lower() == 'content-type' for h in headers) if not has_content_type: headers.insert(0, ("Content-Type", "text/plain; charset=utf-8")) return cls(content, status_code, headers)
[文档] @classmethod def file(cls, file_path, status_code=200, headers=None): """ 返回文件响应 """ if not os.path.exists(file_path): return cls("File not found", 404) # 猜测文件的 MIME 类型 from mimetypes import guess_type mime_type, encoding = guess_type(file_path) mime_type = mime_type or "application/octet-stream" # 读取文件内容 with open(file_path, 'rb') as f: content = f.read() headers = headers or [] # 确保设置正确的 Content-Type has_content_type = any(h[0].lower() == 'content-type' for h in headers) if not has_content_type: headers.insert(0, ("Content-Type", mime_type)) # 添加 Content-Disposition 头,使浏览器下载文件 headers.append( ("Content-Disposition", f"attachment; filename={os.path.basename(file_path)}") ) headers.append(("Content-Length", str(len(content)))) return cls(content, status_code, headers)
[文档] @classmethod def redirect(cls, url, status_code=302, headers=None): """ 返回重定向响应 """ headers = headers or [] headers.insert(0, ("Location", url)) headers.insert(0, ("Content-Type", "text/html; charset=utf-8")) return cls(f"Redirecting to {url}", status_code, headers)
[文档] @classmethod def error(cls, status_code, message=None, headers=None): """ 返回错误响应 """ status_text = responses.get(status_code, "Unknown Error") message = message or status_text content = DEFAULT_STATUS_MESSAGE % { "code": status_code, "message": message, "explain": status_text, } headers = headers or [] headers.insert(0, ("Content-Type", "text/html; charset=utf-8")) return cls(content, status_code, headers)
[文档] @classmethod def stream(cls, content, status_code=200, headers=None): """ 返回流式响应 Args: content: 可迭代对象,如生成器、迭代器,用于流式返回数据 status_code: HTTP 状态码 headers: 响应头列表 Returns: Response 对象 """ headers = headers or [] # 确保设置正确的 Content-Type has_content_type = any(h[0].lower() == 'content-type' for h in headers) if not has_content_type: headers.insert(0, ("Content-Type", "text/plain; charset=utf-8")) # 对于流式响应,不设置 Content-Length,使用 Transfer-Encoding: chunked has_transfer_encoding = any(h[0].lower() == 'transfer-encoding' for h in headers) if not has_transfer_encoding: headers.append(("Transfer-Encoding", "chunked")) return cls(content, status_code, headers)
[文档] @classmethod def sse(cls, content, status_code=200): """ 返回 Server-Sent Events 流式响应 Args: content: 可迭代对象,如生成器、迭代器,用于流式返回 SSE 数据 status_code: HTTP 状态码 Returns: Response 对象 """ headers = [ ("Content-Type", "text/event-stream"), ("Cache-Control", "no-cache"), ("Connection", "keep-alive") ] return cls.stream(content, status_code, headers)
[文档] @classmethod def file_stream(cls, file_path, status_code=200): """ 流式返回文件 Args: file_path: 文件路径 status_code: HTTP 状态码 Returns: Response 对象 """ from mimetypes import guess_type if not os.path.exists(file_path): return cls.error(404, "File not found") # 猜测文件的 MIME 类型 mime_type, encoding = guess_type(file_path) mime_type = mime_type or "application/octet-stream" def generate_file(): with open(file_path, 'rb') as f: while True: chunk = f.read(1024 * 8) # 每次读取 8KB if not chunk: break yield chunk headers = [ ("Content-Disposition", f"attachment; filename={os.path.basename(file_path)}"), ("Content-Type", mime_type) ] return cls.stream(generate_file(), status_code, headers)
# 别名,保持向后兼容 responses = http_status_codes __all__ = [ 'Response', 'DEFAULT_STATUS_MESSAGE', 'default_content_type', 'json_content_type', 'server_info', 'http_status_codes', ]