HTTP请求向服务器传参方式

请求HttpRequest

提示:

  • 用户发送请求时携带的参数后端需要使用,而不同的发送参数的方式对应了不同的提取参数的方式
  • 所以要学会如何提取参数,我们就需要先了解前端传参数有哪些方式

回想一下,利用HTTP协议向服务器传参有几种途径?

  • 查询字符串数据(query string):
    • 形如:?key1=value1&key2=value2
    • 比如:http://www.meiduo.site/list/115/1/?sort=price中的?sort=price
  • 请求体数据(body):
    • 比如:表单数据、json、......
  • URL路径中的特定部分数据:
    • 比如:http://www.meiduo.site/detail/2/中的/2/
    • 请求地址中的该部分数据,可以在路由中使用正则表达式提取出来
  • 请求头数据:
    • HTTP请求报文中的请求头数据(header)

为了演示请求和响应,我们会新建一个子应用request_response去演示相关内容

1. 提取查询字符串数据

提示:

  • 获取请求路径中的查询字符串参数,形如:?k1=v1&k2=v2
  • 可以通过request.GET属性获取,并返回QueryDict类型的对象
# 注册总路由
urlpatterns = [
    # 用户模块:http://127.0.0.1:8000/users/register/
    path('', include('users.urls')),

    # 请求和响应
    path('', include('request_response.urls')),
]
# 注册子路由
urlpatterns = [
    # 测试提取查询字符串参数:http://127.0.0.1:8000/querystring/?name=zxc&age=18
    path('querystring/', views.QSParamView.as_view()),
]
class QSParamView(View):
    """测试提取查询字符串参数
    http://127.0.0.1:8000/querystring/?name=zxc&age=18
    """

    def get(self, request):
        # 获取查询字符串参数name、age
        name = request.GET.get('name', '小明')
        age = request.GET.get('age', '0')

        return http.HttpResponse('查询字符串参数:%s--%s' % (name, age))

重要提示:

  • 提取查询字符串参数不区分请求方式,即使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串参数。

QueryDict补充:

  • QueryDict是由Django自己封装的一个数据类型,继承自python的字典Dict
  • 它被定义在django.http.QueryDict
  • 它专门用来存储请求中提取的查询字符串参数和请求体参数
    • 即,HttpRequest对象中的属性GET、POST都是QueryDict类型的数据
  • QueryDict的使用:
    # 如果键不存在则返回None值,可以设置默认值进行后续处理
    query_dict.get('键',默认值)
    # 可简写为:
    query_dict['键']
    

2. 提取请求体数据

提示:

  • 可以发送请求体数据的请求方式有:POST、PUT、PATCH、DELETE
  • 请求体数据格式不固定,常见的有:表单类型数据和JSON字符串类型,我们应区别对待

2.1 表单类型请求体数据(Form Data)

前端发送的表单类型的请求体数据,可以通过request.POST属性获取,并返回QueryDict对象。

# 测试提取表单类型请求体数据:http://127.0.0.1:8000/formdata/
path('formdata/', views.FormDataParamView.as_view()),
class FormDataParamView(View):
    """测试提取表单类型请求体参数
    http://127.0.0.1:8000/formdata/
    """

    def post(self, request):
        # 获取表单类型请求体参数中的username、password
        username = request.POST.get('username')
        password = request.POST.get('password')

        return http.HttpResponse('表单类型请求体参数:%s--%s' % (username, password))

重要提示:

  • request.POST只能用来获取POST表单发送的请求体数据

2.2 非表单类型请求体数据(Non-Form Data):JSON

提示:

  • 非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据
  • 然后自己按照具体请求体原始数据的格式(JSON等)进行解析
  • request.body获取的是bytes类型的请求体原始数据

需求:

  • 获取请求体中的如下JSON数据
{
    "username": "张小厨",
    "password": "123"
}

可以进行如下方法操作:

# 测试提取非表单类型请求体参数:http://127.0.0.1:8000/json/
path('json/', views.JSONParamView.as_view()),
import json

class JSONParamView(View):
    """测试提取非表单类型请求体参数
    http://127.0.0.1:8000/json/
    """

    def post(self, request):
        # 获取请求体中原始的JSON数据
        json_str = request.body
        # 使用json模块将原始的JSON数据转字典
        json_dict = json.loads(json_str)

        # 提取JSON数据中的参数
        username = json_dict.get('username')
        password = json_dict.get('password')

        return http.HttpResponse('非表单类型请求体参数:%s--%s' % (username, password))

3. URL路径参数:提取URL路径中的特定部分数据

提示:

  • 在定义路由时,可以从URL中获取特定部分的路径参数
  • Django的路由系统会将提取的路径参数传递到视图的内部
  • path()和re_path()都可以提取路径参数

需求:

  • 需求1:http://127.0.0.1:8000/url_param1/18/
    • 提取路径中的数字18
  • 需求2:http://127.0.0.1:8000/url_param2/18500001111/
    • 提取路径中的手机号18500001111

3.1 path()提取路径参数

实现需求1

# 测试path()提取普通路径参数:http://127.0.0.1:8000/url_param1/18/
path('url_param1//', views.URLParam1View.as_view()),
class URLParam1View(View):
    """测试path()提取普通路径参数
    http://127.0.0.1:8000/url_param1/18/
    """

    def get(self, request, age):
        """
        :param age: 路由提取的关键字参数
        """
        return http.HttpResponse('测试path()提取普通路径参数:%s' % age)

重要提示:

  • 路由中提取路径参数时,使用的关键字,必须跟视图中参数名一致

思考:

  • 实现需求1时提取age数字的是什么?

结论:

  • 路由转换器
  • Django默认封装了一些正则表达式,用于在path()中要提取路径参数时使用

默认的路由转换器:

  • 位置在django.urls.converters.py
DEFAULT_CONVERTERS = {
    'int': IntConverter(), # 匹配正整数,包含0
    'path': PathConverter(), # 匹配任何非空字符串,包含了路径分隔符
    'slug': SlugConverter(), # 匹配字母、数字以及横杠、下划线组成的字符串
    'str': StringConverter(), # 匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    'uuid': UUIDConverter(), # 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
}

实现需求2

  • http://127.0.0.1:8000/url_param2/18500001111/
    • 提取路径中的手机号18500001111

问题:

  • 默认的路由转换器中,没有专门用来匹配手机号的路由转换器
  • 所以在使用path()实现需求2时,就无法直接使用默认的路由转换器

解决方案:

  • 如果默认的路由转换器无法满足需求时,我们就需要自定义路由转换器

实现需求2:自定义路由转换器

  • 在任意可以被导入的python文件中,都可以自定义路由转换器
  • 比如:在工程根目录下,新建converters.py文件,用于自定义路由转换器

    class MobileConverter:
      """自定义路由转换器:匹配手机号"""
      # 匹配手机号码的正则
      regex = '1[3-9]\d{9}'
    
      def to_python(self, value):
          # 将匹配结果传递到视图内部时使用
          return int(value)
    
      def to_url(self, value):
          # 将匹配结果用于反向解析传值时使用
          return str(value)
    
  • 注册自定义路由转换器

    • 在总路由中,注册自定义路由转换器

      from django.urls import register_converter
      from converters import MobileConverter
      # 注册自定义路由转换器
      # register_converter(自定义路由转换器, '别名')
      register_converter(MobileConverter, 'mobile')
      
      urlpatterns = []
      
  • 使用自定义路由转换器

    # 测试path()中自定义路由转换器提取路径参数:手机号 http://127.0.0.1:8000/url_param2/18500001111/
    path('url_param2/<mobile:phone_num>/', views.URLParam2View.as_view()),
    
    class URLParam2View(View):
      """测试path()中自定义路由转换器提取路径参数:手机号
      http://127.0.0.1:8000/url_param2/18500001111/
      """
    
      def get(self, request, phone_num):
          """
          :param phone_num: 路由提取的关键字参数
          """
          return http.HttpResponse('测试path()提取路径参数手机号:%s' % phone_num)
    

3.2 re_path()提取路径参数

# 测试re_path()提取路径参数:http://127.0.0.1:8000/url_param3/18500001111/
re_path(r'^url_param3/(?P<phone_num>1[3-9]\d{9})/$', views.URLParam3View.as_view()),
class URLParam3View(View):
    """测试re_path()提取路径参数
    http://127.0.0.1:8000/url_param3/18500001111/
    """

    def get(self, request, phone_num):
        """
        :param phone_num: 路由提取的关键字参数
        """
        return http.HttpResponse('测试re_path()提取路径参数:%s' % phone_num)

3.3 path()和re_path()如何选择?

  • path()语法相对简洁一些,如果没有路径参数要提取或者要提取的路径参数可以使用默认的路由转换器实现时,就选择path()。
  • re_path()语法相对复杂一些,但是,如果希望在匹配路由时,由自己编写所有的正则表达式,就选择re_path()。
  • 需要注意的是,在使用re_path()时,网络地址正则表达式一定要写完整,要有严格的开头和结尾

4. 请求头

可以通过request.META属性获取请求头headers中的数据,request.META为字典类型。

常见的请求头如:

  • CONTENT_LENGTH – The length of the request body (as a string).
  • CONTENT_TYPE – The MIME type of the request body.
  • HTTP_ACCEPT – Acceptable content types for the response.
  • 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).

具体使用如:

class HeadersParamView(View):
    """测试提取请求头参数"""

    def get(self, request):
        # 获取请求头中文件的类型
        ret = request.META.get('CONTENT_TYPE')
        return http.HttpResponse('OK')

5. 其他常用HttpRequest对象属性

  • method:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET'、'POST'。
  • FILES:一个类似于字典的对象,包含所有的上传文件。
  • COOKIES:一个字符串,包含了浏览器自动发送的cookie缓存数据。
  • user:请求中认证出来的用户对象。
posted @ 2021-01-18 23:24  hello_Ms_w  阅读(2727)  评论(0编辑  收藏  举报