我遇到了一个奇怪的问题,即我无法对不受基于GraphQL控制的REST服务执行简单的POST请求。
问题是,无论我在httpMethod
类的URLRequest
属性中设置什么,它始终使用GET
。
我已经做了一些测试,以解决一些问题。例如,我在“请求”中设置了标头,然后可以验证标头是否已发送到服务器(已通过Charles Proxy验证)。
这是您可以粘贴并在Playground中运行的代码:
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请求。我尝试将其更改为下载任务,但是发生了同样的问题。因此我的代码肯定有问题,但我找不到它。
编辑:
每个请求,这是Postman使用的请求。我已将请求导出到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}}'
答案 0 :(得分:1)
我看到的唯一区别是您的游乐场中的http://
和邮递员中的https://
。
因此,只需将http://
中的https://
替换为Playground
。