You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xcode模拟器请求Django后端报403 Forbidden,Canvas预览正常求助

问题描述

用SwiftUI开发登录页面,后端基于Django框架,两者均在本地运行。登录接口在Postman和Xcode Canvas预览中可正常工作,但在Xcode模拟器中运行时返回403 Forbidden错误。此前功能正常,未修改代码却突然失效,关闭Django的CSRF中间件后问题仍存在。

SwiftUI代码

func sendOTP(phoneNumber: String) -> Bool{
    if phoneNumber.count != 11 {
        return false
    }
    guard let url=URL(string: "http://127.0.0.1:8000/login/") else {
        return false
//        fatalError("Missing URL")
    }
    var urlRequest = URLRequest(url: url)
    urlRequest.httpMethod = "POST"
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    
    // Most likely you want to add some token here
    // request.setValue("token", forHTTPHeaderField: "Authorization")
    // request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let requestData = [
        "request_type": "sms_auth",
        "phone_number": phoneNumber,
    ]
    
    let encoder = JSONEncoder()
    if let encodedData = try? encoder.encode(requestData) {
        urlRequest.httpBody = encodedData
    } else {
        return false
    }
    
    let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
        if let error = error {
            print("Request error: ", error)
            return
        }
        guard let response = response as? HTTPURLResponse else {return}
        
        if response.statusCode == 200 {
            guard let data = data else {return}
            DispatchQueue.main.async {
                do {
                    let decodedData = try JSONDecoder().decode(KCWebRes<String>.self, from: data)
                } catch let error {
                    print("Error decoding: ", error)
                }
            }
        }
    }
    dataTask.resume()
    
    return true
}

Django代码

class Login(APIView):
    def post(self, request):

        if not "phont_number" not in request.data:
            pass

        phone_number = request.data["phone_number"]
        if not KCUser.phone_number_auth(phone_number):
            ret = login_sc.fail
            return JsonResponse(ret)

        con_redis = get_redis_connection(alias='verify_codes')

        request_type = request.data["request_type"]
        if request_type == "sms_auth":
            sms_code = "%06d" % random.randint(0, 999999)
            if con_redis.exists("sms_send_" + phone_number):
                ret = login_sc.sms_fail
                return JsonResponse(ret)

            rds_pl = con_redis.pipeline()
            try:
                rds_pl.setex("sms_send_" + phone_number, 60, 1)  
                rds_pl.setex("sms_code_" + phone_number, 300, sms_code)
                rds_pl.execute()
            except Exception as e:
                print(e)

            sms_auth(phone_number, sms_code)
            ret = login_sc.sms_sent
            return JsonResponse(ret)

        elif request_type == "login":
            request_sms_code = request.data["sms_code"]
            _code = con_redis.get("sms_code_" + phone_number).decode()
            if not request_sms_code or not _code:
                ret = login_sc.sms_code_error
                return JsonResponse(ret)

            if request_sms_code != _code:
                ret = login_sc.sms_code_error
                return JsonResponse(ret)
            else:
                if KCUser.objects.filter(phone_number=phone_number).count() == 0:
                    user = KCUser.objects.create_user(
                        phone_number=phone_number,
                        username=phone_number,
                        password="qazwsxedc"  # TODO password
                    )
                    user.save()
                else:
                    user = KCUser.objects.get(phone_number=phone_number)

                login(request, user)
                ret = login_sc.success
                ret["data"] = KCUserSerializer(user).data
                return JsonResponse(ret)

        state = login_sc.fail
        return JsonResponse(state)
解决方案

1. 修正Django代码中的拼写与逻辑错误

你的Django代码存在两处明显问题:

  • 拼写错误:"phont_number"应为"phone_number"
  • 逻辑错误:if not "phont_number" not in request.data:的双重否定逻辑混乱,正确写法应为if "phone_number" not in request.data:

修正后的代码片段:

def post(self, request):
    if "phone_number" not in request.data:
        ret = login_sc.fail
        return JsonResponse(ret)
    
    phone_number = request.data["phone_number"]
    # 后续代码保持不变

该错误可能导致后端处理请求时逻辑异常,返回非预期的错误状态码。

2. 替换模拟器访问的IP地址

iOS模拟器中,127.0.0.1指向模拟器自身而非你的电脑,无法访问本地Django服务。需替换为电脑的局域网IP地址:

  • 查看电脑局域网IP:Mac在系统设置-网络中查看,Windows通过ipconfig命令获取
  • 修改SwiftUI代码中的URL:
guard let url=URL(string: "http://你的电脑IP:8000/login/") else {
    return false
}

3. 重置iOS模拟器的网络状态

模拟器缓存或网络配置可能异常,重置步骤:

  1. 打开Xcode,点击顶部菜单Window > Devices and Simulators
  2. 选中当前使用的模拟器,右键选择Erase All Content and Settings
  3. 确认后重启模拟器再测试

4. 配置Django的跨域访问(CORS)

即使关闭CSRF中间件,Django默认限制跨域请求,模拟器请求可能被判定为跨域。解决方法:

  1. 安装django-cors-headers:
pip install django-cors-headers
  1. 在Django的settings.py中添加配置:
INSTALLED_APPS = [
    # 其他应用
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 放在中间件列表最前面
    # 其他中间件
]

CORS_ALLOW_ALL_ORIGINS = True  # 开发环境临时使用,生产环境需指定具体域名

5. 查看Django详细日志

启动Django服务时,终端会输出请求的详细日志,查看403错误的具体原因(如CSRF验证细节、权限限制等),可快速定位问题。


内容的提问来源于stack exchange,提问作者wyhn.w

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 21:07:25