Django接收自定义http header过程详解


Posted in Python onAugust 23, 2019

add by zhj: Django将所有http header(包括你自定义的http header)都放在了HttpRequest.META这个Python标准字典中,当然HttpRequest.META

中还包含其它一些键值对,这些键值对是Django加进去的,如SERVER_PORT等。对于http header,Django进行了重命名,规则如下

(1) 所有header名大写,将连接符“-”改为下划线“_”

(2) 除CONTENT_TYPE和CONTENT_LENGTH,其它的header名称前加“HTTP_”前缀

参见 https://docs.djangoproject.com/en/1.6/ref/request-response/#django.http.HttpRequest.META

我个人比较喜欢跟踪源代码来查看,源代码如下,

class WSGIRequestHandler(BaseHTTPRequestHandler):

  server_version = "WSGIServer/" + __version__

  def get_environ(self):
    env = self.server.base_environ.copy()
    env['SERVER_PROTOCOL'] = self.request_version
    env['REQUEST_METHOD'] = self.command
    if '?' in self.path:
      path,query = self.path.split('?',1)
    else:
      path,query = self.path,''

    env['PATH_INFO'] = urllib.unquote(path)
    env['QUERY_STRING'] = query

    host = self.address_string()
    if host != self.client_address[0]:
      env['REMOTE_HOST'] = host
    env['REMOTE_ADDR'] = self.client_address[0]

    if self.headers.typeheader is None:
      env['CONTENT_TYPE'] = self.headers.type
    else:
      env['CONTENT_TYPE'] = self.headers.typeheader

    length = self.headers.getheader('content-length')
    if length:
      env['CONTENT_LENGTH'] = length

    for h in self.headers.headers:
      k,v = h.split(':',1)
      k=k.replace('-','_').upper(); v=v.strip()
      if k in env:
        continue          # skip content length, type,etc.
      if 'HTTP_'+k in env:
        env['HTTP_'+k] += ','+v   # comma-separate multiple headers
      else:
        env['HTTP_'+k] = v
    return env

  def get_stderr(self):
    return sys.stderr

  def handle(self):
    """Handle a single HTTP request"""

    self.raw_requestline = self.rfile.readline()
    if not self.parse_request(): # An error code has been sent, just exit
      return

    handler = ServerHandler(
      self.rfile, self.wfile, self.get_stderr(), self.get_environ()
    )
    handler.request_handler = self   # backpointer for logging
    handler.run(self.server.get_app())
class WSGIRequest(http.HttpRequest):
  def __init__(self, environ):
    script_name = base.get_script_name(environ)
    path_info = base.get_path_info(environ)
    if not path_info:
      # Sometimes PATH_INFO exists, but is empty (e.g. accessing
      # the SCRIPT_NAME URL without a trailing slash). We really need to
      # operate as if they'd requested '/'. Not amazingly nice to force
      # the path like this, but should be harmless.
      path_info = '/'
    self.environ = environ
    self.path_info = path_info
    self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
    self.META = environ
    self.META['PATH_INFO'] = path_info
    self.META['SCRIPT_NAME'] = script_name
    self.method = environ['REQUEST_METHOD'].upper()
    _, content_params = self._parse_content_type(self.META.get('CONTENT_TYPE', ''))
    if 'charset' in content_params:
      try:
        codecs.lookup(content_params['charset'])
      except LookupError:
        pass
      else:
        self.encoding = content_params['charset']
    self._post_parse_error = False
    try:
      content_length = int(self.environ.get('CONTENT_LENGTH'))
    except (ValueError, TypeError):
      content_length = 0
    self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
    self._read_started = False
    self.resolver_match = None

WSGIRequest类实例化方法__init__(self,environ)中第二个参数就是WSGIRequestHandler.get_environ()方法返回的数据

WSGIRequest.META在environ的基础上加了一些键值对

用Django做后台,客户端向Django请求数据,为了区分不同的请求,想把每个请求类别加在HTTP头部(headers)里面。

先做实验,就用Python的httplib库来做模拟客户端,参考网上写出模拟代码如下:

#coding=utf8
import httplib
httpClient = None
try:
  myheaders = { "category": "Books",
         "id": "21",
         'My-Agent': "Super brower"
       }
  httpClient = httplib.HTTPConnection('10.14.1XX.XXX',8086,timeout=30)
  httpClient.request('GET','/headinfo/',headers=myheaders)
  response = httpClient.getresponse()
  print response.status
  print response.reason
  print response.read()
except Exception, e:
  print e
finally:
  if httpClient:
    httpClient.close()

其中'/headinfo/'为服务器的响应目录。

然后是服务端的响应代码,《The Django Book》第七章有个获取META的例子:

# GOOD (VERSION 2)
def ua_display_good2(request):
  ua = request.META.get('HTTP_USER_AGENT', 'unknown')
  return HttpResponse("Your browser is %s" % ua)

正好看过这个例子,就模拟上面的这个写了一个能够返回客户端自定义头部的模块:

from django.http import HttpResponse
def headinfo(request):
  category = request.META.get('CATEGORY', 'unkown')
  id = request.META.get('ID','unkown')
  agent = request.META.get('MY-AGENT','unkown')
  html = "<html><body>Category is %s, id is %s, agent is %s</body></html>" % (category, id, agent)
  return HttpResponse(html)

运行结果如下:

$python get.py
#输出:
#200
#OK
#<html><body>Category is unkown, id is unkown, agent is unkown</body></html>

可以看到服务器成功响应了,但是却没有返回自定义的内容。

我以为是客户端模拟headers出问题了,查找和试验了许多次都没有返回正确的结果。后来去查Django的文档,发现了相关的描述:

HttpRequest.META

A standard Python dictionary containing all available HTTP headers. Available headers depend on the client and server, but here are some examples:

  • CONTENT_LENGTH ? the length of the request body (as a string).
  • CONTENT_TYPE ? the MIME type of the request body.
  • HTTP_ACCEPT_ENCODING ? Acceptable encodings for the response.
  • HTTP_ACCEPT_LANGUAGE ? Acceptable languages for the response.
  • HTTP_HOST ? The HTTP Host header sent by the client.
  • HTTP_REFERER ? The referring page, if any.
  • HTTP_USER_AGENT ? The client's user-agent string.
  • QUERY_STRING ? The query string, as a single (unparsed) string.
  • REMOTE_ADDR ? The IP address of the client.
  • REMOTE_HOST ? The hostname of the client.
  • REMOTE_USER ? The user authenticated by the Web server, if any.
  • REQUEST_METHOD ? A string such as "GET" or "POST".
  • SERVER_NAME ? The hostname of the server.
  • SERVER_PORT ? The port of the server (as a string).

With the exception of CONTENT_LENGTH and CONTENT_TYPE, as given above, any HTTP headers in the request are converted toMETA keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_ prefix to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER.

其中红色的部分说明是说除了两个特例之外,其他的头部在META字典中的key值都会被加上“HTTP_”的前缀,终于找到问题所在了,赶紧修改服务端代码:

category = request.META.get('HTTP_CATEGORY', 'unkown')
id = request.META.get('HTTP_ID','unkown')

果然,执行后返回了想要的结果:

$python get.py
#正确的输出:
#200
#OK
#<html><body>Category is Books, id is 21, agent is Super brower</body></html>

得到的经验就是遇到问题要多查文档,搜索引擎并不一定比文档更高效。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中每次处理一个字符的5种方法
May 21 Python
Python中的ctime()方法使用教程
May 22 Python
Python 快速实现CLI 应用程序的脚手架
Dec 05 Python
python opencv 直方图反向投影的方法
Feb 24 Python
对Python的zip函数妙用,旋转矩阵详解
Dec 13 Python
python实现三维拟合的方法
Dec 29 Python
python basemap 画出经纬度并标定的实例
Jul 09 Python
python删除文件夹下相同文件和无法打开的图片
Jul 16 Python
python爬虫刷访问量 2019 7月
Aug 01 Python
Python+redis通过限流保护高并发系统
Apr 15 Python
Python unittest基本使用方法代码实例
Jun 29 Python
Python Selenium操作Cookie的实例方法
Feb 28 Python
Python 处理文件的几种方式
Aug 23 #Python
python 数据生成excel导出(xlwt,wlsxwrite)代码实例
Aug 23 #Python
python @classmethod 的使用场合详解
Aug 23 #Python
python 一篇文章搞懂装饰器所有用法(建议收藏)
Aug 23 #Python
python 类的继承 实例方法.静态方法.类方法的代码解析
Aug 23 #Python
Python中最好用的命令行参数解析工具(argparse)
Aug 23 #Python
详解Python并发编程之创建多线程的几种方法
Aug 23 #Python
You might like
深入php多态的实现详解
2013/06/09 PHP
php读取3389的脚本
2014/05/06 PHP
thinkphp跨库操作的简单代码实例
2016/09/22 PHP
jquery关于页面焦点的定位(文本框获取焦点时改变样式 )
2010/09/10 Javascript
js操作IE浏览器弹出浏览文件夹可以返回目录路径
2014/07/14 Javascript
jQuery提交多个表单的小技巧
2014/07/27 Javascript
JavaScript插件化开发教程(五)
2015/02/01 Javascript
Jquery操作Ajax方法小结
2015/11/29 Javascript
Javascript基础之数组的使用
2016/05/13 Javascript
jQuery实现的可编辑表格完整实例
2016/06/20 Javascript
Javascript字符串常用方法详解
2016/07/21 Javascript
JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)
2016/09/05 Javascript
javascript事件捕获机制【深入分析IE和DOM中的事件模型】
2016/12/15 Javascript
Node.js使用cookie保持登录的方法
2018/05/11 Javascript
在vue中v-bind使用三目运算符绑定class的实例
2018/09/29 Javascript
JavaScript实现省市联动效果
2019/11/22 Javascript
利用Python脚本在Nginx和uwsgi上部署MoinMoin的教程
2015/05/05 Python
Python实现获取磁盘剩余空间的2种方法
2017/06/07 Python
python3实现指定目录下文件sha256及文件大小统计
2019/02/25 Python
使用TensorFlow对图像进行随机旋转的实现示例
2020/01/20 Python
python 如何调用 dubbo 接口
2020/09/24 Python
CSS3 中的@keyframes介绍
2014/09/02 HTML / CSS
送给他或她的礼物:FUN.com
2018/08/17 全球购物
世界上最受欢迎的花店:1-800-Flowers.com
2020/06/01 全球购物
学前教育毕业生自荐信
2013/10/29 职场文书
信息技术专业个人自我评价
2013/12/11 职场文书
女方回门宴答谢词
2014/01/14 职场文书
幼儿园大班教学反思
2014/02/10 职场文书
分层教学实施方案
2014/03/19 职场文书
员工工作自我评价
2014/09/26 职场文书
2015年扫黄打非工作总结
2015/05/13 职场文书
李强感恩观后感
2015/06/17 职场文书
春节随笔
2015/08/15 职场文书
jupyter notebook保存文件默认路径更改方法汇总(亲测可以)
2021/06/09 Python
ORACLE查看当前账号的相关信息
2021/06/18 Oracle
Mysql表数据比较大情况下修改添加字段的方法实例
2022/06/28 MySQL