整体架构概览 类继承关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 HttpResponseBase (six.Iterator) ├── HttpResponse ├── StreamingHttpResponse │ └── FileResponse └── HttpResponseRedirectBase ├── HttpResponseRedirect └── HttpResponsePermanentRedirect HttpResponse ├── HttpResponseNotModified ├── HttpResponseBadRequest ├── HttpResponseForbidden ├── HttpResponseNotFound ├── HttpResponseNotAllowed ├── HttpResponseGone ├── HttpResponseServerError └── JsonResponse
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 ): self ._headers = {} self ._closable_objects = [] self ._handler_class = None 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.' ) 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 : pass except UnicodeError as e: if mime_encode: 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 if max_age is not None : self .cookies[key]['max-age' ] = max_age 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 ): """设置流式内容""" 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 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) 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) 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) 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 ): 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 = {} 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 ): return HttpResponse('<h1>Hello World</h1>' ) 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 ): 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响应处理能力。