iOS开发中URLComponents查询参数内@符号未编码的解决方法问询
iOS 中 URLComponents 实现查询参数@正确编码的方案
问题本质
- 系统默认
URLComponents的参数转义逻辑遵循RFC 3986宽松规则,@属于URL查询段内允许直接保留的合法字符,默认不会做转义处理。 - Alamofire 采用更严格的查询值转义策略,会把所有不属于查询分隔符的特殊字符(包括@)统一编码,因此和原生组件的输出存在行为差异。
- 提前手动给查询值做编码会触发双重编码:因为
queryItems属性内部会对传入的参数值再走一次系统默认转义流程,把已经生成的%转成%25,最终会出现%2540这类后端无法正确解析的字符串。
正确实现方式
核心思路是绕过系统的宽松转义流程,通过percentEncodedQueryItems属性传入已经按规则完成编码的参数,完全避免二次转义:
- 自定义转义规则的字符集:在系统提供的
CharacterSet.urlQueryAllowed基础上,把需要强制转义的字符(比如@)从允许保留的集合中移除,此时编码过程就会自动把这类字符转成对应的百分号格式。 - 对每个查询参数的name、value,按照自定义的字符集规则做一次百分号编码。
- 将编码后的参数数组赋值给
percentEncodedQueryItems,该属性不会对传入的内容做任何二次修改,只会按照标准格式用&、=拼接查询串。
示例代码:
import UIKit func createUrl() { // 自定义转义用的字符集:从默认查询允许集合里移除@,强制对@做转义 var forcedEscapeSet = CharacterSet.urlQueryAllowed forcedEscapeSet.remove(charactersIn: "@") var components = URLComponents() components.scheme = "https" components.host = "services-test.abc.pippo.com" components.path = "/first/second/third" let rawItems: [URLQueryItem] = [ URLQueryItem(name: "amznToken", value: "asfdsfadfasads"), URLQueryItem(name: "UID", value: "sdsadasdasdasd"), URLQueryItem(name: "login_ID", value: "pippo.pluto@gmail.com"), URLQueryItem(name: "ssoEnabled", value: "true"), URLQueryItem(name: "country", value: "IT") ] // 手动完成参数编码后,赋值给percentEncodedQueryItems避免二次转义 components.percentEncodedQueryItems = rawItems.map { item in let encodedName = item.name.addingPercentEncoding(withAllowedCharacters: forcedEscapeSet) ?? item.name let encodedValue = item.value?.addingPercentEncoding(withAllowedCharacters: forcedEscapeSet) ?? "" return URLQueryItem(name: encodedName, value: encodedValue) } if let url = components.url { print(url.absoluteString) // 输出结果:https://services-test.abc.pippo.com/first/second/third?amznToken=asfdsfadfasads&UID=sdsadasdasdasd&login_ID=pippo.pluto%40gmail.com&ssoEnabled=true&country=IT } } createUrl()
避坑提示
- 不要在给
queryItems赋值前对字符串做任何编码操作,必然触发双重编码问题。 - 使用
percentEncodedQueryItems时,必须保证传入的参数值已经完成所有规则要求的转义,该属性不会帮你处理未转义的特殊字符(比如未转义的&、=会直接破坏查询串结构)。 - 不要用简单字符串替换的方式把@换成%40,遇到参数本身包含百分号的场景会出现逻辑错误,优先使用系统内置的字符集编码方法。
内容的提问来源于stack exchange,提问作者AR89
相关产品推荐
相关产品推荐

