登录注册分析、多方式登录接口

一、登录注册页面分析

# 根据原型图分析出:要写的功能
    # 用户名密码登录接口
    # 注册功能接口
    # 手机号验证码登录接口
    # 发送短信验证码接口
    # 验证手机号是否存在接口

二、路由

总路由

path('api/v1/user/', include('user.urls')),

分路由:user/urls.py

from django.urls import path
from . import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('userinfo', views.UserView, 'userinfo')

urlpatterns = [

]
urlpatterns += router.urls

三、验证手机号是否存在接口

3.1 不同方式验证手机号

from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from .models import User

"""方式一"""
class UserView(ViewSet):
    # 验证手机号是否存在的接口 ---> get请求 ---> 要操作数据库,但不需要序列化 --> 想要自动生成路由
    # ---> 继承ViewSet
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        # get请求,手机号会拼在路径上
        # 取出前端传入的手机号
        mobile = request.query_patams.get('mobile', None)
        if mobile:
            user = User.objects.filter(mobile=mobile).first()
            if user:
                return APIResponse(msg='手机号存在')
            else:
                return APIResponse(code=101, msg='手机号不存在')
        else:
            return APIResponse(code=101, msg='手机号没传')

        
        
"""方式二:"""
from rest_framework.exceptions import APIException

class UserView(ViewSet):
    # 验证手机号是否存在的接口 ---> get请求 ---> 要操作数据库,但不需要序列化 --> 想要自动生成路由
    # ---> 继承ViewSet
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        # get请求,手机号会拼在路径上
        # 取出前端传入的手机号
        mobile = request.query_params.get('mobile', None)
        if mobile:
            user = User.objects.filter(mobile=mobile).first()
            if user:
                return APIResponse(msg='手机号存在')
        # 前端只要接收到显示状态码不是100,就是手机号没有注册或者手机号错误或者没输手机号
        # 对错误,都可以抛异常
        raise APIException('手机号不存在')
        # return APIResponse('手机号不存在')
        
"""方式三:"""
class UserView(ViewSet):
    # 验证手机号是否存在的接口 ---> get请求 ---> 要操作数据库,但不需要序列化 --> 想要自动生成路由
    # ---> 继承ViewSet
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        # get请求,手机号会拼在路径上
        # 取出前端传入的手机号
        mobile = request.query_params.get('mobile', None)
        if mobile:
            user = User.objects.filter(mobile=mobile).first()
            if user:
                return APIResponse(msg='手机号存在')
        # 只有手机号存在,并且手机号正确,才会返回数据,手机号不正确就会直接抛异常,就会被全局异常捕获住,后面的情况也就不需要写代码了
        

3.2 固定写法

from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from .models import User
from rest_framework.exceptions import APIException
class UserView(ViewSet):
    # 验证手机号是否存在的接口 ---> get请求 ---> 要操作数据库,但不需要序列化 --> 想要自动生成路由
    # ---> 继承ViewSet
    @action(methods=['GET'], detail=False)
    def check_mobile(self, request, *args, **kwargs):
        # get请求,手机号会拼在路径上
        # 取出前端传入的手机号
        try:
            # 可以把主要代码写在try中,产生的错误可以主动抛出
            mobile = request.query_params.get('mobile')
            print(mobile)
            User.objects.get(mobile=mobile)  # 使用get,有且只有一条才不报错,如果没有或多余一条,就报错
            return APIResponse(msg='手机号存在')
        except Exception as e:
            raise APIException('手机号不存在')

四、后端多方式登录接口

user/views.py

@action(methods=['POST'], detail=False)
    # 多方式登录接口---> 要不要序列化类---> 要用序列化类---> 继承的视图类基类
    # post请求--->前端携带的数据 {username:xxx,password:123}
    def mul_login(self, request, *args, **kwargs):
        # 校验逻辑写在序列化类中
        ser = self.get_serializer(data=request.data)
        # 只要执行它,就会执行 字段自己的校验规则,局部钩子,全局钩子(全局钩子中写验证逻辑,生成token的逻辑)
        ser.is_valid(raise_exception=True)  # 如果校验失败,会主动抛出异常
        username = ser.context.get('username')
        icon = ser.context.get('icon')
        token = ser.context.get('token')
        return APIResponse(username=username, token=token, icon=icon)

user/serializers.py

from .models import User
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.db.models import Q
from rest_framework.exceptions import APIException, ValidationError
from django.conf import settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class LoginUserSerializer(serializers.ModelSerializer):
    # 字段自己的规则,会走唯一性校验--->就过不了---->必须要重写该字段
    username = serializers.CharField(required=True)

    class Meta:
        model = User
        fields = ['username', 'password']  # 只做数据校验--->写校验的字段

    def _get_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        # user=User.objects.filter(username=username || mobile=username || email=username)
        user = User.objects.filter(Q(username=username) | Q(mobile=username) | Q(email=username)).first()
        if user and user.check_password(password):
            # 说明用户名存在,密码再校验
            return user
        else:
            raise APIException('用户名或密码错误')

    def _get_token(self, user):
        # jwt的签发
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token

    def validate(self, attrs):
        # 验证用户名密码逻辑---->签发token逻辑
        # username字段可能是 用户  ,手机号,邮箱--->正则匹配--->换一种方式 使用Q查询
        user = self._get_user(attrs)
        # 签发token
        token = self._get_token(user)
        # 把用户,token放在 序列化类的context中【上下文】
        # self.username = user.username    # 这样写容易污染ser原本的数据属性
        self.context['username'] = user.username
        # user.icon是个文件对象,直接获取user.icon.url,而传给前端的需要写全地址
        self.context[ 'icon'] = settings.BACKEND_URL + user.icon.url
        self.context['token'] = token

        return attrs

posted @ 2023-06-28 21:48  星空看海  阅读(71)  评论(0编辑  收藏  举报