Django HTTP Response

整体架构概览

类继承关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 基础抽象类
HttpResponseBase (six.Iterator)
├── HttpResponse # 标准HTTP响应
├── StreamingHttpResponse # 流式HTTP响应
│ └── FileResponse # 文件响应优化
└── HttpResponseRedirectBase # 重定向响应基类
├── HttpResponseRedirect # 临时重定向 (302)
└── HttpResponsePermanentRedirect # 永久重定向 (301)

# 具体状态码响应类
HttpResponse
├── HttpResponseNotModified # 304
├── HttpResponseBadRequest # 400
├── HttpResponseForbidden # 403
├── HttpResponseNotFound # 404
├── HttpResponseNotAllowed # 405
├── HttpResponseGone # 410
├── HttpResponseServerError # 500
└── JsonResponse # JSON响应

HttpResponseBase 基础类详解

1. 核心设计思想

1
2
3
4
5
6
7
class HttpResponseBase(six.Iterator):
"""
HTTP响应基类,提供字典式的头部访问接口
这个类不处理内容,不应直接使用
应该使用HttpResponse和StreamingHttpResponse子类
"""
status_code = 200

设计特点

  • 继承自 six.Iterator,支持迭代器协议
  • 只处理HTTP头部和状态码,不处理响应体
  • 提供统一的HTTP响应接口

2. 初始化过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def __init__(self, content_type=None, status=None, reason=None, charset=None):
# _headers: 存储HTTP头部的映射
# 键是小写的头部名称,值是(原始大小写名称, 头部值)的元组
self._headers = {}

# 需要关闭的对象列表(如文件句柄)
self._closable_objects = []

# 处理器类(用于request_finished信号)
self._handler_class = None

# Cookie容器
self.cookies = SimpleCookie()

# 关闭状态标志
self.closed = False

# 状态码验证
if status is not None:
try:
self.status_code = int(status)
except (ValueError, TypeError):
raise TypeError('HTTP status code must be an integer.')

if not 100 <= self.status_code <= 599:
raise ValueError('HTTP status code must be an integer from 100 to 599.')

# 设置默认Content-Type
if content_type is None:
content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE, self.charset)
self['Content-Type'] = content_type

关键特性

  • 状态码验证:确保状态码在100-599范围内
  • 字符集处理:自动设置默认字符集
  • 资源管理:维护需要关闭的对象列表

3. HTTP头部处理

3.1 字典式接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def __setitem__(self, header, value):
"""设置HTTP头部"""
header = self._convert_to_charset(header, 'ascii')
value = self._convert_to_charset(value, 'latin-1', mime_encode=True)
# 存储:小写键 -> (原始键, 值)
self._headers[header.lower()] = (header, value)

def __getitem__(self, header):
"""获取HTTP头部值"""
return self._headers[header.lower()][1]

def __delitem__(self, header):
"""删除HTTP头部"""
try:
del self._headers[header.lower()]
except KeyError:
pass

def has_header(self, header):
"""检查是否存在某个头部(大小写不敏感)"""
return header.lower() in self._headers

设计亮点

  • 大小写不敏感:HTTP头部名称不区分大小写
  • 原始格式保留:保持原始的大小写格式,兼容老系统
  • 编码安全:自动处理ASCII和Latin-1编码

3.2 编码转换机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def _convert_to_charset(self, value, charset, mime_encode=False):
"""将头部键/值转换为ascii/latin-1本地字符串"""
if not isinstance(value, (bytes, six.text_type)):
value = str(value)

# 防止头部注入攻击
if ((isinstance(value, bytes) and (b'\n' in value or b'\r' in value)) or
isinstance(value, six.text_type) and ('\n' in value or '\r' in value)):
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)

try:
if six.PY3:
if isinstance(value, str):
value.encode(charset) # 验证编码
else:
value = value.decode(charset) # 解码字节串
else:
# Python 2处理逻辑
pass
except UnicodeError as e:
if mime_encode:
# MIME编码处理非ASCII字符
value = str(Header(value, 'utf-8', maxlinelen=sys.maxsize).encode())
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise
return value

安全特性

  • 头部注入防护:检测并阻止换行符注入
  • 编码验证:确保头部值符合HTTP标准
  • MIME编码支持:自动处理非ASCII字符

4. Cookie处理机制

4.1 基本Cookie设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
domain=None, secure=False, httponly=False):
"""设置Cookie"""
value = force_str(value)
self.cookies[key] = value

# 处理过期时间
if expires is not None:
if isinstance(expires, datetime.datetime):
if timezone.is_aware(expires):
expires = timezone.make_naive(expires, timezone.utc)
delta = expires - expires.utcnow()
# 添加一秒确保时间匹配
delta = delta + datetime.timedelta(seconds=1)
expires = None
max_age = max(0, delta.days * 86400 + delta.seconds)
else:
self.cookies[key]['expires'] = expires

# 设置各种Cookie属性
if max_age is not None:
self.cookies[key]['max-age'] = max_age
# IE需要expires,如果没有设置则自动添加
if not expires:
self.cookies[key]['expires'] = cookie_date(time.time() + max_age)

if path is not None:
self.cookies[key]['path'] = path
if domain is not None:
self.cookies[key]['domain'] = domain
if secure:
self.cookies[key]['secure'] = True
if httponly:
self.cookies[key]['httponly'] = True

4.2 签名Cookie支持

1
2
3
4
5
6
7
8
9
def set_signed_cookie(self, key, value, salt='', **kwargs):
"""设置签名Cookie,防止篡改"""
value = signing.get_cookie_signer(salt=key + salt).sign(value)
return self.set_cookie(key, value, **kwargs)

def delete_cookie(self, key, path='/', domain=None):
"""删除Cookie(通过设置过期时间为过去)"""
self.set_cookie(key, max_age=0, path=path, domain=domain,
expires='Thu, 01-Jan-1970 00:00:00 GMT')

安全考虑

  • 签名验证:防止客户端篡改Cookie值
  • 盐值支持:增强签名安全性
  • 安全属性:支持Secure和HttpOnly标志

5. 资源管理

1
2
3
4
5
6
7
8
9
10
def close(self):
"""关闭响应,清理资源"""
for closable in self._closable_objects:
try:
closable.close()
except Exception:
pass
self.closed = True
# 发送请求完成信号
signals.request_finished.send(sender=self._handler_class)

重要性

  • 文件句柄清理:确保文件等资源正确释放
  • 信号发送:通知Django请求处理完成
  • 异常安全:即使关闭失败也不影响其他资源

HttpResponse 标准响应类

1. 内容管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class HttpResponse(HttpResponseBase):
"""带有字符串内容的HTTP响应类"""
streaming = False

def __init__(self, content=b'', *args, **kwargs):
super(HttpResponse, self).__init__(*args, **kwargs)
self.content = content

@property
def content(self):
"""获取完整内容"""
return b''.join(self._container)

@content.setter
def content(self, value):
"""设置内容,支持迭代器"""
if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
# 处理迭代器内容
content = b''.join(self.make_bytes(chunk) for chunk in value)
if hasattr(value, 'close'):
try:
value.close()
except Exception:
pass
else:
content = self.make_bytes(value)

# 创建正确编码的字节串列表
self._container = [content]

设计特点

  • 统一字节处理:所有内容最终转换为字节串
  • 迭代器支持:可以接受迭代器作为内容
  • 资源清理:自动关闭可关闭的内容对象

2. 可写接口

1
2
3
4
5
6
7
8
9
10
11
12
def write(self, content):
"""追加内容到响应"""
self._container.append(self.make_bytes(content))

def writelines(self, lines):
"""写入多行内容"""
for line in lines:
self.write(line)

def writable(self):
"""指示响应是否可写"""
return True

3. 序列化

1
2
3
def serialize(self):
"""完整的HTTP消息,包括头部,作为字节串"""
return self.serialize_headers() + b'\r\n\r\n' + self.content

StreamingHttpResponse 流式响应

1. 设计目标

1
2
3
4
5
6
7
8
class StreamingHttpResponse(HttpResponseBase):
"""
带有迭代器内容的流式HTTP响应类

只应该迭代一次,当响应流式传输到客户端时
但是可以被追加或替换为包装原始内容的新迭代器
"""
streaming = True

适用场景

  • 大文件下载
  • 实时数据流
  • 内存敏感的响应

2. 流式内容处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@property
def streaming_content(self):
"""流式内容属性"""
return map(self.make_bytes, self._iterator)

@streaming_content.setter
def streaming_content(self, value):
self._set_streaming_content(value)

def _set_streaming_content(self, value):
"""设置流式内容"""
# 确保永远不能在"value"上迭代超过一次
self._iterator = iter(value)
if hasattr(value, 'close'):
self._closable_objects.append(value)

def __iter__(self):
return self.streaming_content

@property
def content(self):
"""流式响应没有content属性"""
raise AttributeError(
"This %s instance has no `content` attribute. Use "
"`streaming_content` instead." % self.__class__.__name__
)

关键特性

  • 一次性迭代:防止重复消费流内容
  • 延迟处理:内容按需生成和传输
  • 资源管理:自动管理流对象的生命周期

FileResponse 文件响应优化

1. 文件特化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class FileResponse(StreamingHttpResponse):
"""为文件优化的流式HTTP响应类"""
block_size = 4096 # 4KB块大小

def _set_streaming_content(self, value):
if hasattr(value, 'read'):
# 文件对象处理
self.file_to_stream = value
filelike = value
if hasattr(filelike, 'close'):
self._closable_objects.append(filelike)
# 创建分块读取迭代器
value = iter(lambda: filelike.read(self.block_size), b'')
else:
self.file_to_stream = None
super(FileResponse, self)._set_streaming_content(value)

优化特性

  • 分块读取:避免大文件一次性加载到内存
  • WSGI优化file_to_stream属性可被WSGI服务器优化使用
  • 自动清理:确保文件句柄正确关闭

重定向响应系列

1. 基础重定向类

1
2
3
4
5
6
7
8
9
10
11
12
13
class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp']

def __init__(self, redirect_to, *args, **kwargs):
super(HttpResponseRedirectBase, self).__init__(*args, **kwargs)
self['Location'] = iri_to_uri(redirect_to)

# 安全检查:验证重定向URL的协议
parsed = urlparse(force_text(redirect_to))
if parsed.scheme and parsed.scheme not in self.allowed_schemes:
raise DisallowedRedirect("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)

url = property(lambda self: self['Location'])

安全特性

  • 协议白名单:只允许安全的URL协议
  • 开放重定向防护:防止恶意重定向攻击

2. 具体重定向类

1
2
3
4
5
class HttpResponseRedirect(HttpResponseRedirectBase):
status_code = 302 # 临时重定向

class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
status_code = 301 # 永久重定向

状态码响应类

1. 特殊状态处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class HttpResponseNotModified(HttpResponse):
status_code = 304

def __init__(self, *args, **kwargs):
super(HttpResponseNotModified, self).__init__(*args, **kwargs)
# 304响应不应该有Content-Type头
del self['content-type']

@HttpResponse.content.setter
def content(self, value):
"""304响应不能有内容"""
if value:
raise AttributeError("You cannot set content to a 304 (Not Modified) response")
self._container = []

2. 方法不允许响应

1
2
3
4
5
6
7
class HttpResponseNotAllowed(HttpResponse):
status_code = 405

def __init__(self, permitted_methods, *args, **kwargs):
super(HttpResponseNotAllowed, self).__init__(*args, **kwargs)
# 必须包含Allow头,列出允许的方法
self['Allow'] = ', '.join(permitted_methods)

JsonResponse JSON响应

1. JSON特化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class JsonResponse(HttpResponse):
"""消费数据并序列化为JSON的HTTP响应类"""

def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
# 安全检查:默认只允许dict对象
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)

if json_dumps_params is None:
json_dumps_params = {}

# 设置JSON内容类型
kwargs.setdefault('content_type', 'application/json')

# 序列化数据
data = json.dumps(data, cls=encoder, **json_dumps_params)
super(JsonResponse, self).__init__(content=data, **kwargs)

安全特性

  • 类型限制:默认只允许dict类型,防止数组JSON劫持
  • 自定义编码器:支持Django的JSON编码器
  • 内容类型自动设置:确保正确的MIME类型

工具函数和异常

1. 异常类

1
2
3
4
5
6
7
class BadHeaderError(ValueError):
"""HTTP头部格式错误"""
pass

class Http404(Exception):
"""404错误的快捷异常"""
pass

2. 字符集检测

1
2
3
4
5
6
7
8
9
10
11
12
_charset_from_content_type_re = re.compile(r';\s*charset=(?P<charset>[^\s;]+)', re.I)

@property
def charset(self):
"""从Content-Type头中提取字符集"""
if self._charset is not None:
return self._charset
content_type = self.get('Content-Type', '')
matched = _charset_from_content_type_re.search(content_type)
if matched:
return matched.group('charset').replace('"', '')
return settings.DEFAULT_CHARSET

设计模式和原则

1. 模板方法模式

  • HttpResponseBase:定义响应处理的基本流程
  • 子类:实现具体的内容处理逻辑

2. 策略模式

  • 不同响应类型:标准、流式、文件等不同处理策略
  • 编码器:可插拔的JSON编码器

3. 装饰器模式

  • 头部处理:统一的头部设置和验证
  • 编码转换:透明的字符编码处理

4. 资源管理模式

  • RAII原则:通过_closable_objects自动管理资源
  • 上下文管理:确保资源正确释放

实际应用场景

1. 基本Web响应

1
2
3
4
5
6
7
8
9
def view(request):
# 简单HTML响应
return HttpResponse('<h1>Hello World</h1>')

# JSON API响应
return JsonResponse({'status': 'success', 'data': data})

# 重定向
return HttpResponseRedirect('/login/')

2. 文件下载

1
2
3
4
5
6
def download_file(request):
# 大文件下载
file_obj = open('large_file.pdf', 'rb')
response = FileResponse(file_obj)
response['Content-Disposition'] = 'attachment; filename="file.pdf"'
return response

3. 流式数据

1
2
3
4
5
6
7
8
9
def streaming_csv(request):
# 大量数据的CSV导出
def generate_csv():
for row in large_dataset:
yield f"{row['id']},{row['name']}\n"

response = StreamingHttpResponse(generate_csv(), content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="data.csv"'
return response

总结

Django的HTTP响应系统体现了以下设计理念:

安全第一

  • 头部注入防护:防止HTTP头部注入攻击
  • 重定向安全:协议白名单防止恶意重定向
  • JSON安全:默认类型限制防止劫持攻击
  • Cookie安全:签名验证和安全标志支持

性能优化

  • 流式处理:支持大文件和实时数据流
  • 延迟处理:按需生成和传输内容
  • 内存管理:分块处理避免内存溢出
  • 资源清理:自动管理文件句柄等资源

扩展性强

  • 类继承体系:清晰的继承关系支持定制
  • 可插拔组件:自定义编码器和处理器
  • 标准兼容:完整的HTTP协议支持

易用性好

  • 字典式接口:直观的头部访问方式
  • 多种响应类型:覆盖常见的Web开发需求
  • 自动处理:编码、时区等细节的自动处理

通过精心设计的类继承体系和丰富的功能特性,Django的响应系统为Web应用开发提供了强大、安全、高效的HTTP响应处理能力。


Django HTTP Response
https://luffy997.github.io/2025/07/07/Django-HTTP-Response/
作者
Luffy997
发布于
2025年7月7日
许可协议