如何在Django REST Framework项目中集成sms.to发送OTP?
在Django REST Framework中集成sms.to发送OTP的实现方案
因为目前没有官方或第三方维护的sms.to Python包,直接调用其REST API是最直接的方案,以下是具体步骤:
1. 准备工作
- 登录sms.to后台获取你的API密钥,确保账户有足够余额或已完成身份验证
- 在Django项目的
settings.py中配置参数,建议用环境变量存储敏感信息:# settings.py import os SMSTO_API_KEY = os.environ.get('SMSTO_API_KEY') SMSTO_SENDER_ID = 'YourAppName' # 替换为你在sms.to后台设置的发件人ID
2. 封装发送OTP的工具函数
创建utils/sms.py文件,封装API调用逻辑:
# utils/sms.py import requests from django.conf import settings def send_otp(phone_number, otp_code): url = "https://api.sms.to/sms/send" headers = { "Authorization": f"Bearer {settings.SMSTO_API_KEY}", "Content-Type": "application/json" } payload = { "to": phone_number, # 格式要求:带国家码,如+8613xxxxxxxxx "from": settings.SMSTO_SENDER_ID, "message": f"Your OTP code is {otp_code}. Valid for 5 minutes." } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return {"success": True, "data": response.json()} except requests.exceptions.RequestException as e: # 可根据需求添加日志记录 return {"success": False, "error": str(e)}
3. 创建OTP存储模型
在对应app的models.py中添加OTP记录模型:
# models.py from django.db import models from django.utils import timezone import uuid class OTP(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) phone_number = models.CharField(max_length=20) otp_code = models.CharField(max_length=6) created_at = models.DateTimeField(auto_now_add=True) is_used = models.BooleanField(default=False) def is_expired(self): # 设置5分钟有效期,可按需调整 return timezone.now() > self.created_at + timezone.timedelta(minutes=5) class Meta: indexes = [ models.Index(fields=['phone_number', 'otp_code']), ]
执行迁移命令生效:
python manage.py makemigrations python manage.py migrate
4. 实现DRF视图处理OTP发送与验证
发送OTP视图
# views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import OTP from .utils.sms import send_otp import random class SendOTPView(APIView): def post(self, request): phone_number = request.data.get('phone_number') if not phone_number: return Response({"error": "Phone number is required"}, status=status.HTTP_400_BAD_REQUEST) # 生成6位随机OTP otp_code = str(random.randint(100000, 999999)) # 调用发送函数 sms_response = send_otp(phone_number, otp_code) if not sms_response['success']: return Response({"error": sms_response['error']}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # 清理旧OTP并保存新记录 OTP.objects.filter(phone_number=phone_number).delete() OTP.objects.create(phone_number=phone_number, otp_code=otp_code) return Response({"message": "OTP sent successfully"}, status=status.HTTP_200_OK)
验证OTP视图
class VerifyOTPView(APIView): def post(self, request): phone_number = request.data.get('phone_number') otp_code = request.data.get('otp_code') if not phone_number or not otp_code: return Response({"error": "Phone number and OTP are required"}, status=status.HTTP_400_BAD_REQUEST) try: otp_record = OTP.objects.get(phone_number=phone_number, otp_code=otp_code, is_used=False) except OTP.DoesNotExist: return Response({"error": "Invalid OTP"}, status=status.HTTP_400_BAD_REQUEST) if otp_record.is_expired(): return Response({"error": "OTP has expired"}, status=status.HTTP_400_BAD_REQUEST) # 标记OTP为已使用,防止重复验证 otp_record.is_used = True otp_record.save() # 此处可添加后续业务逻辑,如生成登录Token、创建用户等 return Response({"message": "OTP verified successfully"}, status=status.HTTP_200_OK)
5. 配置URL路由
在app的urls.py中添加路由映射:
# urls.py from django.urls import path from .views import SendOTPView, VerifyOTPView urlpatterns = [ path('send-otp/', SendOTPView.as_view(), name='send-otp'), path('verify-otp/', VerifyOTPView.as_view(), name='verify-otp'), ]
注意事项
- 必须保证手机号带正确的国家码,否则sms.to API会拒绝请求
- 生产环境建议对发送OTP的接口添加频率限制,防止恶意刷取
- 可根据业务需求调整OTP的长度和有效期
- 建议添加日志模块,记录短信发送的成功/失败状态,方便排查问题
内容的提问来源于stack exchange,提问作者mr_fahad
相关产品推荐
相关产品推荐

