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 相关文章推荐
使用Python3编写抓取网页和只抓网页图片的脚本
Aug 20 Python
Python3.2模拟实现webqq登录
Feb 15 Python
对pandas进行数据预处理的实例讲解
Apr 20 Python
python字符串常用方法
Jun 14 Python
django实现将后台model对象转换成json对象并传递给前端jquery
Mar 16 Python
keras 回调函数Callbacks 断点ModelCheckpoint教程
Jun 18 Python
python和php哪个更适合写爬虫
Jun 22 Python
Android Q之气泡弹窗的实现示例
Jun 23 Python
Tensorflow tensor 数学运算和逻辑运算方式
Jun 30 Python
python实现移动木板小游戏
Oct 09 Python
Alpine安装Python3依赖出现的问题及解决方法
Dec 25 Python
Python 第三方库 openpyxl 的安装过程
Dec 24 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中将地址生成迅雷快车旋风链接的代码[测试通过]
2011/04/20 PHP
php+mysqli使用预处理技术进行数据库查询的方法
2015/01/28 PHP
php代码检查代理ip的有效性
2016/08/19 PHP
php微信公众号开发(2)百度BAE搭建和数据库使用
2016/12/15 PHP
JQuery EasyUI 对话框的使用方法
2010/10/24 Javascript
通过Javascript将数据导出到外部Excel文档的函数代码
2012/06/15 Javascript
jquery使用ajax实现微信自动回复插件
2014/04/28 Javascript
jQuery支持动态参数将函数绑定到事件上的方法
2015/03/17 Javascript
jquery实现点击查看更多内容控制段落文字展开折叠效果
2015/08/06 Javascript
js带前后翻页的图片切换效果代码分享
2015/09/08 Javascript
ionic中列表项增加和删除的实现方法
2017/01/22 Javascript
详解vue2父组件传递props异步数据到子组件的问题
2017/06/29 Javascript
jQuery中extend函数简单用法示例
2017/10/11 jQuery
vue 文件目录结构详解
2017/11/24 Javascript
通过vue提供的keep-alive减少对服务器的请求次数
2018/04/01 Javascript
Angular4.x Event (DOM事件和自定义事件详解)
2018/10/09 Javascript
微信小程序如何修改radio和checkbox的默认样式和图标
2019/07/24 Javascript
在layui.use 中自定义 function 的正确方法
2019/09/16 Javascript
Vue.js标签页组件使用方法详解
2019/10/19 Javascript
javascript设计模式 ? 状态模式原理与用法实例分析
2020/04/22 Javascript
Ant Design的Table组件去除
2020/10/24 Javascript
[00:09]DOTA2全国高校联赛 精彩活动引爆全场
2018/05/30 DOTA
python获取当前计算机cpu数量的方法
2015/04/18 Python
python去除所有html标签的方法
2015/05/05 Python
pytorch 改变tensor尺寸的实现
2020/01/03 Python
python中的socket实现ftp客户端和服务器收发文件及md5加密文件
2020/04/01 Python
收藏!10个免费高清视频素材网站!【设计、视频剪辑必备】
2021/03/18 杂记
用HTML5实现手机摇一摇的功能的教程
2012/10/30 HTML / CSS
HTML5中的autofocus(自动聚焦)属性介绍
2014/04/23 HTML / CSS
canvas仿写贝塞尔曲线的示例代码
2017/12/29 HTML / CSS
为您的家、后院、车库等在线购物:Spreetail
2019/06/17 全球购物
会计职业生涯规划书
2014/01/13 职场文书
简历中的自我评价怎么写
2014/01/29 职场文书
政治思想表现评语
2014/05/04 职场文书
个人作风建设自查报告
2014/10/22 职场文书
早会开场白台词大全
2015/06/01 职场文书