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

调用GitHub Gist API出现422错误的排查及解决方案

调用GitHub API创建Gist时遇到422错误的排查与解决

问题背景

调用GitHub API将JSON文档存储为Gist,严格遵循API规范实现后收到422错误。该错误表示请求格式合法但服务器无法处理,已确认:

  • Auth token有效(否则会返回权限相关错误)
  • JSON格式通过验证,且用工具调用API可成功创建Gist
  • 请求头按文档推荐设置
  • 未触发限流

但代码实现始终返回422错误,文档说明该错误表示“验证失败,或端点被滥用”,未滥用服务,需排查具体原因。

初始报错代码

struct GistPayload : Codable {
    var Public : Bool
    var description : String
    var files : Dictionary<String, Dictionary<String, ProjectExport>>
}

@objc func exportProject() {
    guard let projectExport = userProject?.export() else { return }
    let src = GistPayload(Public: true, description: "testing Gist upload", files: ["testGist" : ["content" : projectExport]])
    sendToGist(payload: src)
}

func encode(src : GistPayload) -> String? {
    let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(src)
        return String(data: data, encoding: .utf8)!
    } catch {print("problem \(error)")}
   return nil
}

func sendToGist(payload : GistPayload) {
    guard var data = encode(src: payload) else {
        print("no data")
        return
    }
    data = data.replacingOccurrences(of: "Public", with: "public")
    let url = URL(string: "https://api.github.com/gists")!
    var request = URLRequest(url: url)
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.setValue("application/vnd.github+json", forHTTPHeaderField: "Accept")
    request.setValue("Bearer \(StartController.token)", forHTTPHeaderField: "Authorization")
    request.httpMethod = "POST"
    request.httpBody = data.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard
            let data = data,
            let response = response as? HTTPURLResponse, error == nil
        else {                                                              
            print("error", error ?? URLError(.badServerResponse))
            return
        }
        
        guard (200 ... 299) ~= response.statusCode else {                    
            print("statusCode should be 2xx, but is \(response.statusCode)")
            print("response = \(response)")
            return
        }
        do {
            print("done")
        } catch {
            print(error) // parsing error
            if let responseString = String(data: data, encoding: .utf8) {
                print("responseString = \(responseString)")
            } else {
                print("unable to parse response as string")
            }
        }
    }
    task.resume()
}

更新后的代码仍报错

调整结构后代码如下,但仍返回422错误:

struct GistData {
    var filename : String
    var description : String
    var publc : Bool
    var payload : ProjectExport
    
    func files() ->   Dictionary<String, Dictionary<String, ProjectExport>> {
        var rslt : Dictionary<String, Dictionary<String, ProjectExport>>
        return [filename : ["content" : payload]]
    }
    
    func output() -> Output {
        return Output(description: description, Public: publc, files: files())
    }
    
    struct Output : Codable {
        var description : String
        var Public : Bool
        var files : Dictionary<String, Dictionary<String, ProjectExport>>
    }
}

@objc func exportProject() {
    guard let projectExport = userProject?.export() else { return }
    var gistData = GistData(filename: "newThingie.json", description: "a thing", publc: true, payload: projectExport)
    sendToGist(payload: gistData.output())
}

func encode(src : GistData.Output) -> String? {
    let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(src)
        return String(data: data, encoding: .utf8)!
    } catch {print("problem \(error)")}
   return nil
}

错误响应:

statusCode should be 2xx, but is 422

响应详情:

response = <NSHTTPURLResponse: 0x600001d48d40> { URL: https://api.github.com/gists } { Status Code: 422, Headers {
"Access-Control-Allow-Origin" =     (
"*"
);
"Content-Length" =     (
38981
);
"Content-Type" =     (
"application/json; charset=utf-8"
);
Date =     (
"Tue, 22 Nov 2022 04:10:48 GMT"
);
Server =     (
"GitHub.com"
);
"Strict-Transport-Security" =     (
"max-age=31536000; includeSubdomains; preload"
);
Vary =     (
"Accept-Encoding, Accept, X-Requested-With"
);
"access-control-expose-headers" =     (
"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset"
);
"content-security-policy" =     (
"default-src 'none'"
);
"github-authentication-token-expiration" =     (
"2023-02-20 03:06:05 UTC"
);
"referrer-policy" =     (
"origin-when-cross-origin, strict-origin-when-cross-origin"
);
"x-accepted-oauth-scopes" =     (
""
);
"x-content-type-options" =     (
nosniff
);
"x-frame-options" =     (
deny
);
"x-github-media-type" =     (
"github.v3; format=json"
);
"x-github-request-id" =     (
"D032:5EB9:94899C:9949C2:637C4BC8"
);
"x-oauth-scopes" =     (
gist
);
"x-ratelimit-limit" =     (
5000
);
"x-ratelimit-remaining" =     (
4992
);
"x-ratelimit-reset" =     (
1669090408
);
"x-ratelimit-resource" =     (
core
);
"x-ratelimit-used" =     (
8
);
"x-xss-protection" =     (
0
);
} }

问题原因

  1. Content-Type设置错误:初始代码中Content-Type被设置为application/x-www-form-urlencoded,但发送的是JSON格式数据,不符合API要求,需改为application/json。
  2. Content值类型错误:之前的代码直接将ProjectExport对象作为content的值嵌套编码,导致最终JSON中content是一个嵌套对象,而GitHub API要求content必须是字符串类型。需先将ProjectExport编码为JSON字符串,再作为content的值传入。

解决方案代码

以下是可正常运行的代码:

struct Tester : Codable {
    var description : String
    var publc : Bool
    var filename : String
    var payload : String
    
    func files() -> Dictionary<String, Dictionary<String, String>> {
        return [filename : ["content" : payload]]
    }
    
    func output() -> Output {
        return Output(description: description, Public: publc, files: files())
    }
                
    struct Output : Codable {
        var description : String
        var Public : Bool
        var files :  Dictionary<String, Dictionary<String, String>>
    }
}

@objc func exportProject() {
    guard let projectExport = userProject?.export() else { return }
    guard var encodedData = encode(src: projectExport) else {
        print("problem encoding data")
        return
    }
    var gistTest = Tester(description: "please work", publc: true, filename: "justMaybe.md", payload: encodedData)
    sendToGist(payload: gistTest.output())
}

func encode(src : ProjectExport) -> String? {
    let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(src)
        return String(data: data, encoding: .utf8)!
    } catch {print("problem \(error)")}
   return nil
}

func encode(src : Tester.Output) -> String? {
    let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(src)
        return String(data: data, encoding: .utf8)!
    } catch {print("problem \(error)")}
   return nil
}

func sendToGist(payload : Tester.Output) {
    guard var data = encode(src: payload) else {
        print("no data")
        return
    }
    let url = URL(string: "https://api.github.com/gists")!
    var request = URLRequest(url: url)
    request.httpBody = data.data(using: String.Encoding.utf8)
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("application/vnd.github+json", forHTTPHeaderField: "Accept")
    request.setValue("Bearer \(StartController.token)", forHTTPHeaderField: "Authorization")
    request.httpMethod = "POST"
   

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard
            let data = data,
            let response = response as? HTTPURLResponse, error == nil
        else {                                                               // 检查基础网络错误
            print("error", error ?? URLError(.badServerResponse))
            return
        }
        
        guard (200 ... 299) ~= response.statusCode else {                    // 检查HTTP错误
            print("statusCode should be 2xx, but is \(response.statusCode)")
            print("response = \(response)")
            return
        }
        do {
            print("done")
        } catch {
            print(error) // 解析错误
            if let responseString = String(data: data, encoding: .utf8) {
                print("responseString = \(responseString)")
            } else {
                print("unable to parse response as string")
            }
        }
    }
    task.resume()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 09:01:21