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

Swift调用.NET C# Web API报415错误 JWT解析异常求助

问题根因与修复方案

1. 415 Unsupported Media Type 错误修复

你当前代码的核心问题是重复设置了两次Content-Type请求头:

  • 第一次设置为application/x-www-form-urlencoded
  • 第二次设置为application/json

虽然setValue(_:forHTTPHeaderField:)逻辑上应该覆盖前一个值,但iOS部分版本的URLRequest实现中,对同一个头字段重复赋值会出现值拼接问题,最终请求头的Content-Type会变成application/x-www-form-urlencoded, application/json这种非法格式,ASP.NET Core的内容协商模块无法识别该类型,直接返回415。
另外你当前HTTP方法写为"Post"(首字母大写、其余小写),不符合HTTP规范全大写的要求,部分反向代理或服务端配置下会出现请求识别异常。
修复后的请求配置代码如下:

var request = URLRequest(url: url)
// 删除多余的form-urlencoded头设置,仅保留JSON类型声明
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// 增加Accept头,明确告知服务端期望返回JSON格式响应
request.setValue("application/json", forHTTPHeaderField: "Accept")
// HTTP方法必须使用全大写
request.httpMethod = "POST"

// 序列化请求体时移除.prettyPrinted选项,避免生成无意义的格式化缩进增加请求体积
do {
    request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch {
    print(error.localizedDescription)
    return
}

2. JWT令牌解码失败修复

你当前的响应模型存在两个不符合Swift规范的问题,导致无法正常解码:

  • 用于JSON解码的模型必须遵守Codable(或Decodable)协议,你的AuthenticationResponse类未遵守该协议,无法直接从二进制数据反序列化
  • 接口返回的结构包含多个嵌套字段、可选值字段,你仅定义了jwtToken一个属性,默认解码器会因字段匹配失败抛出错误
  • 不建议让网络响应模型直接继承ObservableObject,网络层应该返回纯数据模型,视图层再根据需要转换为可观察对象

修正后的响应模型代码:

import Foundation

// 先定义嵌套的错误对象模型,所有可能返回null的字段都声明为可选类型
struct ApiError: Codable {
    let eventName: String?
    let errorMessage: String?
    let errorDate: String?
    let statusCode: Int?
    let json: String?
}

// 登录响应模型优先用结构体(值类型),符合Swift编码规范,自动支持Codable
struct AuthenticationResponse: Codable {
    let id: String
    let firstName: String
    let lastName: String
    let username: String
    let playerId: Int
    let jwtToken: String
    let error: ApiError?
    let refreshToken: String?
}

请求成功回调中的解码逻辑替换为:

do {
    let decoder = JSONDecoder()
    // 如果后端返回蛇形命名的字段,可打开下面这行配置自动转驼峰,你当前给出的示例是驼峰命名,不需要开启
    // decoder.keyDecodingStrategy = .convertFromSnakeCase
    let authResponse = try decoder.decode(AuthenticationResponse.self, from: data)
    // 直接从强类型模型中取JWT即可
    let jwtToken = authResponse.jwtToken
    print("获取到JWT令牌:\(jwtToken)")
} catch {
    print("解码失败:\(error)")
    if let responseString = String(data: data, encoding: .utf8) {
        print("原始响应内容:\(responseString)")
    }
}

注意:网络回调默认运行在后台线程,如果拿到令牌后需要更新UI,必须手动切换到主线程执行,逻辑和C#中WPF/MAUI需要切UI线程更新界面一致。


你咨询的两个问题解答

1. URLSession的复用注意事项

和C# HttpClient的使用习惯完全相反:Swift中强烈建议复用URLSession实例,不要每次请求都新建。

  • URLSession底层会自动管理TCP连接池、TLS会话缓存、DNS缓存更新,每次新建实例反而会导致频繁握手、连接无法复用,大幅降低性能,甚至引发请求泄漏
  • 通用场景直接用URLSession.shared全局单例即可;如果需要自定义超时、请求拦截、证书校验等配置,可以创建一个全局持有的URLSession实例在整个App生命周期复用,不会出现C#中老版本HttpClient长期复用导致的DNS不更新问题,苹果底层已经自动处理了该逻辑
  • URLSession不需要手动释放,ARC会自动管理内存,没有类似C#的Dispose要求

2. 简化网络请求的第三方库推荐

优先推荐Alamofire,是Swift生态最成熟的网络库,对URLSession做了全链路封装,内置JSON编解码、请求拦截、上传下载、错误统一处理等能力,和C#生态的RestSharp体验类似,可以减少60%以上的样板代码。
如果你的API已经开启Swagger,也可以直接用Swagger代码生成工具输出Swift端的请求模型和接口调用代码,不需要手写任何网络逻辑。如果不想引入第三方依赖,基于URLSession写一个通用的泛型封装,处理统一的编解码、错误逻辑,也能达到很简洁的使用效果。


C#转Swift开发的小建议
  • 定义数据模型优先用结构体(struct)而非类(class),结构体是值类型,没有继承的额外开销,对Codable的支持更友好,和C#中record类型的使用体验类似
  • JSON解析优先用JSONDecoder+Codable强类型解码,不要用JSONSerialization转字典再手动取值,类型安全性更高,和C#中System.Text.Json反序列化到强类型模型的逻辑一致
  • Swift命名规范和C#基本一致,类型名用大驼峰、方法/变量名用小驼峰,你当前代码中CallWebApi方法名首字母大写不符合规范,建议改为callWebApi
  • JWT等敏感凭证不要存在UserDefaults中,应该存入系统钥匙串,避免被轻易读取。

内容的提问来源于stack exchange,提问作者dotnetdevcsharp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:42:18