我遇到了一个奇怪的问题,在这个问题中,我无法对一个基于graphql而无法控制的rest服务执行简单的post请求。
问题是,不管我在httpMethod
类的URLRequest
属性中设置了什么,它总是使用GET
来代替。
我做了一些测试来排除一些问题。例如,我在请求中设置了一个头,我可以验证该头是否被发送到服务器(使用charles proxy验证)。
这是可以在操场上粘贴和运行的代码:
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution = true
let url = URL(string: "http://graphql.anilist.co/")!
let internalSession = URLSession(configuration: .default)
var request = URLRequest(url: url)
request.httpMethod = "POST"
let headers = ["content-type": "application/json"]
request.allHTTPHeaderFields = headers
request.httpBody =
"""
{"query":"query (\n\t$season: MediaSeason,\n\t$year: Int,\n\t$format: MediaFormat,\n\t$excludeFormat: MediaFormat,\n\t$status: MediaStatus,\n\t$minEpisodes: Int,\n\t$page: Int,\n){\n\tPage(page: $page) {\n\t\tpageInfo {\n\t\t\thasNextPage\n\t\t\ttotal\n\t\t}\n\t\tmedia(\n\t\t\tseason: $season\n\t\t\tseasonYear: $year\n\t\t\tformat: $format,\n\t\t\tformat_not: $excludeFormat,\n\t\t\tstatus: $status,\n\t\t\tepisodes_greater: $minEpisodes,\n\t\t\tisAdult: false,\n\t\t\ttype: ANIME,\n\t\t\tsort: TITLE_ROMAJI,\n\t\t) {\n\t\t\t\nid\nidMal\ntitle {\n\tromaji\n\tnative\n\tenglish\n}\nstartDate {\n\tyear\n\tmonth\n\tday\n}\nendDate {\n\tyear\n\tmonth\n\tday\n}\nstatus\nseason\nformat\ngenres\nsynonyms\nduration\npopularity\nepisodes\nsource(version: 2)\ncountryOfOrigin\nhashtag\naverageScore\nsiteUrl\ndescription\nbannerImage\ncoverImage {\n\textraLarge\n\tcolor\n}\ntrailer {\n\tid\n\tsite\n\tthumbnail\n}\nexternalLinks {\n\tsite\n\turl\n}\nrankings {\n\trank\n\ttype\n\tseason\n\tallTime\n}\nstudios(isMain: true) {\n\tnodes {\n\t\tid\n\t\tname\n\t\tsiteUrl\n\t}\n}\nrelations {\n\tedges {\n\t\trelationType(version: 2)\n\t\tnode {\n\t\t\tid\n\t\t\ttitle {\n\t\t\t\tromaji\n\t\t\t\tnative\n\t\t\t\tenglish\n\t\t\t}\n\t\t\tsiteUrl\n\t\t}\n\t}\n}\n\nairingSchedule(\n\tnotYetAired: true\n\tperPage: 2\n) {\n\tnodes {\n\t\tepisode\n\t\tairingAt\n\t}\n}\n\n\t\t}\n\t}\n}","variables":{"season": WINTER,"year": 2019,"page": 1, "perPage": 100}}
""".data(using: .utf8)
print("THE REQUEST \(String(describing: request.httpMethod))")
let task = internalSession.dataTask(with: request, completionHandler: { (data, response, error) in
if let e = error {
print("ERROR: \(e)")
} else if let response = response as? HTTPURLResponse {
print("THE RESPONSE: \(response)")
let json = try! JSONSerialization.jsonObject(with: data!, options: [])
print(json)
}
})
task.resume()
(请忽略所有强制展开选项并强制
try!
,这是测试代码)。预期结果
我希望web服务返回类似于以下内容的json(简化):
{
"data": {
"Page": {
"pageInfo": {
"hasNextPage": true,
"total": 81
},
"media": [
{
"id": 102882,
"idMal": 37956,
"title": {
"romaji": "3D Kanojo: Real Girl 2",
"native": "3D彼女 リアルガール 2",
"english": "Real Girl 2"
},
"startDate": {
"year": 2019,
"month": 1,
"day": 9
},
"endDate": {
"year": null,
"month": null,
"day": null
},
"status": "RELEASING",
"season": "WINTER",
"format": "TV",
"genres": [
"Romance",
"Slice of Life",
"Comedy"
],
"synonyms": [],
"duration": 23,
"popularity": 3298,
"episodes": 12,
"source": "MANGA",
"countryOfOrigin": "JP",
"hashtag": "#3D彼女リアルガール #3D彼女",
"averageScore": 61,
"siteUrl": "https://anilist.co/anime/102882",
"description": "The second season of <i>3D Kanojo</i>.",
"bannerImage": null,
"coverImage": {
"extraLarge": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/nx102882-lKp3ExWNzoE6.jpg",
"color": "#e4bb93"
},
"trailer": {
"id": "x6yokzp",
"site": "dailymotion",
"thumbnail": "https://www.dailymotion.com/thumbnail/video/x6yokzp"
},
"externalLinks": [
{
"site": "Official Site",
"url": "http://www.3dkanojo-anime.com/"
},
{
"site": "Twitter",
"url": "https://twitter.com/3Dkanojo_anime"
}
],
"rankings": [
{
"rank": 16,
"type": "RATED",
"season": null,
"allTime": false
},
{
"rank": 19,
"type": "POPULAR",
"season": null,
"allTime": false
},
{
"rank": 16,
"type": "RATED",
"season": "WINTER",
"allTime": false
},
{
"rank": 15,
"type": "POPULAR",
"season": "WINTER",
"allTime": false
}
],
"studios": {
"nodes": [
{
"id": 346,
"name": "Hoods Entertainment",
"siteUrl": "https://anilist.co/studio/346"
}
]
},
"relations": {
"edges": [
{
"relationType": "PREQUEL",
"node": {
"id": 100526,
"title": {
"romaji": "3D Kanojo: Real Girl",
"native": "3D彼女 リアルガール",
"english": "Real Girl"
},
"siteUrl": "https://anilist.co/anime/100526"
}
},
{
"relationType": "SOURCE",
"node": {
"id": 80767,
"title": {
"romaji": "3D Kanojo: Real Girl",
"native": "3D彼女 〈リアルガール〉",
"english": "Real Girl"
},
"siteUrl": "https://anilist.co/manga/80767"
}
}
]
},
"airingSchedule": {
"nodes": [
{
"episode": 7,
"airingAt": 1550595540
},
{
"episode": 8,
"airingAt": 1551200340
}
]
}
}
]
}
}
}
获得的结果
相反,我知道:
{
"data": null,
"errors": [
{
"message": "Not Found.",
"hint": "Use POST request to access graphql subdomain.",
"status": 404
}
]
}
服务抱怨请求是get请求,应该是post,我明确告诉urlrequest使用post。如果您在charles或其他代理中查看请求,您将确实看到请求是作为get请求执行的,并且
httpBody
属性被丢弃。如果编辑标题并添加另一个标题,如下所示:let headers = ["content-type": "application/json", "foo": "bar"]
您将在代理服务器中看到正确发送头。
我能得出的唯一结论是,
URLSessionDataTask
存在一个内部问题,迫使只执行get请求。我试着把它改成下载任务,但同样的问题也在发生。所以我的代码肯定有问题,但我找不到。编辑:
根据请求,这是邮递员使用的请求。我已将请求导出到curl,以使其易于导入。
curl -X POST \
https://graphql.anilist.co/ \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 49d7e55e-35c2-4a3c-82f0-4eccb1250fc0' \
-H 'cache-control: no-cache' \
-d '{"query":"query (\n\t$season: MediaSeason,\n\t$year: Int,\n\t$format: MediaFormat,\n\t$excludeFormat: MediaFormat,\n\t$status: MediaStatus,\n\t$minEpisodes: Int,\n\t$page: Int,\n){\n\tPage(page: $page) {\n\t\tpageInfo {\n\t\t\thasNextPage\n\t\t\ttotal\n\t\t}\n\t\tmedia(\n\t\t\tseason: $season\n\t\t\tseasonYear: $year\n\t\t\tformat: $format,\n\t\t\tformat_not: $excludeFormat,\n\t\t\tstatus: $status,\n\t\t\tepisodes_greater: $minEpisodes,\n\t\t\tisAdult: false,\n\t\t\ttype: ANIME,\n\t\t\tsort: TITLE_ROMAJI,\n\t\t) {\n\t\t\t\nid\nidMal\ntitle {\n\tromaji\n\tnative\n\tenglish\n}\nstartDate {\n\tyear\n\tmonth\n\tday\n}\nendDate {\n\tyear\n\tmonth\n\tday\n}\nstatus\nseason\nformat\ngenres\nsynonyms\nduration\npopularity\nepisodes\nsource(version: 2)\ncountryOfOrigin\nhashtag\naverageScore\nsiteUrl\ndescription\nbannerImage\ncoverImage {\n\textraLarge\n\tcolor\n}\ntrailer {\n\tid\n\tsite\n\tthumbnail\n}\nexternalLinks {\n\tsite\n\turl\n}\nrankings {\n\trank\n\ttype\n\tseason\n\tallTime\n}\nstudios(isMain: true) {\n\tnodes {\n\t\tid\n\t\tname\n\t\tsiteUrl\n\t}\n}\nrelations {\n\tedges {\n\t\trelationType(version: 2)\n\t\tnode {\n\t\t\tid\n\t\t\ttitle {\n\t\t\t\tromaji\n\t\t\t\tnative\n\t\t\t\tenglish\n\t\t\t}\n\t\t\tsiteUrl\n\t\t}\n\t}\n}\n\nairingSchedule(\n\tnotYetAired: true\n\tperPage: 2\n) {\n\tnodes {\n\t\tepisode\n\t\tairingAt\n\t}\n}\n\n\t\t}\n\t}\n}","variables":{"year":2019,"season":"WINTER","page":1, "limit": 12}}'
最佳答案
我在这里看到的唯一区别是在你的操场和邮递员。
所以在你的http://
中用https://
替换http://
。
关于swift - URLSessionDataTask忽略URLRequest的httpMethod属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54715318/