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与js)两种实现
Feb 21 Python
python批量提交沙箱问题实例
Oct 08 Python
用Python进行TCP网络编程的教程
Apr 29 Python
Python使用pymysql小技巧
Jun 04 Python
Python实现的根据文件名查找数据文件功能示例
May 02 Python
零基础使用Python读写处理Excel表格的方法
May 02 Python
python爬虫实现中英翻译词典
Jun 25 Python
使用Python轻松完成垃圾分类(基于图像识别)
Jul 09 Python
python集合常见运算案例解析
Oct 17 Python
python 队列基本定义与使用方法【初始化、赋值、判断等】
Oct 24 Python
Django扫码抽奖平台的配置过程详解
Jan 14 Python
深入理解Python变量的数据类型和存储
Feb 01 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
ThinkPHP写数组插入与获取最新插入数据ID实例
2014/11/03 PHP
php使用正则验证中文
2016/04/06 PHP
PHP校验15位和18位身份证号的类封装
2018/11/07 PHP
jQuery仿Flash上下翻动的中英文导航菜单实例
2015/03/10 Javascript
js实现键盘Enter键提交表单的方法
2015/05/27 Javascript
js实现卡片式项目管理界面UI设计效果
2015/12/08 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
2016/08/02 Javascript
JavaScript学习笔记整理_用于模式匹配的String方法
2016/09/19 Javascript
Vue自定义图片懒加载指令v-lazyload详解
2020/12/31 Javascript
默认浏览器设置及vue自动打开页面的方法
2018/09/21 Javascript
详解可以用在VS Code中的正则表达式小技巧
2019/05/14 Javascript
jQuery实现checkbox全选、反选及删除等操作的方法详解
2019/08/02 jQuery
Vue实现购物车实例代码两则
2020/05/30 Javascript
深度解读vue-resize的具体用法
2020/07/08 Javascript
Vue之封装公用变量以及实现方式
2020/07/31 Javascript
[49:05]Newbee vs TNC 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
Python数据结构与算法之图结构(Graph)实例分析
2017/09/05 Python
python+opencv实现阈值分割
2018/12/26 Python
在Python中字典根据多项规则排序的方法
2019/01/21 Python
python自动化实现登录获取图片验证码功能
2019/11/20 Python
带你彻底搞懂python操作mysql数据库(cursor游标讲解)
2020/01/06 Python
TensorFlow实现自定义Op方式
2020/02/04 Python
python图片指定区域替换img.paste函数的使用
2020/04/09 Python
matplotlib 使用 plt.savefig() 输出图片去除旁边的空白区域
2021/01/05 Python
如何用Django处理gzip数据流
2021/01/29 Python
德国baby-markt婴儿用品瑞士网站:baby-markt.ch
2017/06/09 全球购物
美国运动鞋和服装网上商店:YCMC
2018/09/15 全球购物
LODI女鞋在线商店:阿利坎特的鞋类品牌
2019/02/15 全球购物
希腊品牌鞋类销售网站:epapoutsia.gr
2020/03/18 全球购物
银行实习生自我鉴定范文
2013/09/19 职场文书
中介公司区域经理岗位职责范本
2014/03/02 职场文书
推广活动策划方案
2014/08/23 职场文书
2015年清明节活动总结
2015/02/09 职场文书
党员干部公开承诺书范文
2015/04/27 职场文书
Python OpenCV实现图形检测示例详解
2022/04/08 Python
JS实现九宫格拼图游戏
2022/06/28 Javascript