URLSessionDataTask忽略URLRequest的httpMethod属性

时间:2019-02-15 18:40:45

标签: swift http nsurlsession nsurlrequest nsurlsessiondatatask

我遇到了一个奇怪的问题,即我无法对不受基于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}}'

1 个答案:

答案 0 :(得分:1)

我看到的唯一区别是您的游乐场中的http://和邮递员中的https://

因此,只需将http://中的https://替换为Playground