Shopify API文件上传时为何要求Authorization头?
解决Shopify API文件上传的MissingSecurityHeader错误
问题描述
我通过Shopify API上传文件分两步操作:
- 请求临时上传目录,可正常获取所需参数
- 上传文件时收到错误:
MissingSecurityHeaderYour request was missing a required header. Authorization
根据Shopify官方文档,上传无需Authorization头,且文档中的curl示例也未使用该头,为何我的请求会被要求提供该头?以下是我的C#代码:
using Newtonsoft.Json; using ShopifyBulkUploadPoc; var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Post, "https://xxx.myshopify.com/admin/api/2021-07/graphql.json"); request.Headers.Add("X-Shopify-Access-Token", "xxx"); request.Headers.Add("Cookie", "_master_udr=xxx; _shopify_y=xxx; localization=DK; secure_customer_sig="); var content = new StringContent("{\"query\":\"mutation {\\r\\n stagedUploadsCreate(input:{\\r\\n resource: BULK_MUTATION_VARIABLES,\\r\\n filename: \\\"bulk_op_vars\\\",\\r\\n mimeType: \\\"text/jsonl\\\",\\r\\n httpMethod: POST\\r\\n }){\\r\\n userErrors{\\r\\n field,\\r\\n message\\r\\n },\\r\\n stagedTargets{\\r\\n url,\\r\\n resourceUrl,\\r\\n parameters {\\r\\n name,\\r\\n value\\r\\n }\\r\\n }\\r\\n }\\r\\n}\",\"variables\":{}}", null, "application/json"); request.Content = content; var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); Rootobject rootObject = JsonConvert.DeserializeObject<Rootobject>(responseContent); var url = rootObject.data.stagedUploadsCreate.stagedTargets[0].url; var key = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[3].value; var acl = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[2].value; var x_goog_date = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[4].value; var x_goog_credential = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[5].value; var x_goog_algorithm = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[6].value; var x_goog_signature = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[7].value; var policy = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[8].value; var content_type = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[0].value; var success_action_status = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters[1].value; var requestB = new HttpRequestMessage(HttpMethod.Put, "https://shopify-staged-uploads.storage.googleapis.com/"); var contentB = new MultipartFormDataContent(); contentB.Add(new StringContent(key), "key"); contentB.Add(new StringContent(x_goog_credential), "x-goog-credential"); contentB.Add(new StringContent(x_goog_algorithm), "x-goog-algorithm"); contentB.Add(new StringContent(x_goog_date), "x-goog-date"); contentB.Add(new StringContent(x_goog_signature), "x-goog-signature"); contentB.Add(new StringContent(policy), "policy"); contentB.Add(new StringContent(acl), "acl"); contentB.Add(new StringContent(content_type), "Content-Type"); contentB.Add(new StringContent(success_action_status), "success_action_status"); contentB.Add(new StreamContent(File.OpenRead("C:/products.jsonl")), "file", "/C:/products.jsonl"); requestB.Content = content; var responseB = await client.SendAsync(requestB); Console.WriteLine(await responseB.Content.ReadAsStringAsync()); namespace ShopifyBulkUploadPoc { public class Rootobject { public Data data { get; set; } } public class Data { public Stageduploadscreate stagedUploadsCreate { get; set; } } public class Stageduploadscreate { public object[] userErrors { get; set; } public Stagedtarget[] stagedTargets { get; set; } } public class Stagedtarget { public string url { get; set; } public string resourceUrl { get; set; } public Parameter[] parameters { get; set; } } public class Parameter { public string name { get; set; } public string value { get; set; } } }
问题分析与修复
你遇到的错误并非真的缺少Authorization头,而是代码里的两个致命错误导致请求完全不符合要求:
请求方法与URL错误
- 第一步创建staged upload时指定了
httpMethod: POST,但第二步你用了HttpMethod.Put,方法不匹配会导致签名验证失败。 - 上传URL不能直接写根地址,必须使用第一步返回的
url字段(包含签名路径的完整上传地址)。
- 第一步创建staged upload时指定了
内容赋值错误
- 最后一行
requestB.Content = content;是严重错误:你把第一步的GraphQL请求内容赋值给了上传请求,而不是刚构建的带签名参数的contentB,导致上传的内容完全不对,自然触发安全验证错误。
- 最后一行
另外,硬编码参数索引(比如parameters[3])很危险,参数顺序可能会变化,建议通过参数名匹配取值。
修正后的关键代码
// 1. 用第一步返回的完整上传URL,且请求方法和stagedUploadsCreate里的httpMethod一致 var requestB = new HttpRequestMessage(HttpMethod.Post, url); var contentB = new MultipartFormDataContent(); // 2. 改用参数名匹配取值,避免索引硬编码 var parametersDict = rootObject.data.stagedUploadsCreate.stagedTargets[0].parameters .ToDictionary(p => p.name, p => p.value); contentB.Add(new StringContent(parametersDict["key"]), "key"); contentB.Add(new StringContent(parametersDict["x-goog-credential"]), "x-goog-credential"); contentB.Add(new StringContent(parametersDict["x-goog-algorithm"]), "x-goog-algorithm"); contentB.Add(new StringContent(parametersDict["x-goog-date"]), "x-goog-date"); contentB.Add(new StringContent(parametersDict["x-goog-signature"]), "x-goog-signature"); contentB.Add(new StringContent(parametersDict["policy"]), "policy"); contentB.Add(new StringContent(parametersDict["acl"]), "acl"); contentB.Add(new StringContent(parametersDict["Content-Type"]), "Content-Type"); contentB.Add(new StringContent(parametersDict["success_action_status"]), "success_action_status"); // 3. 文件字段只需要文件名,不需要完整路径 contentB.Add(new StreamContent(File.OpenRead("C:/products.jsonl")), "file", "products.jsonl"); // 4. 正确赋值上传内容 requestB.Content = contentB; var responseB = await client.SendAsync(requestB); Console.WriteLine(await responseB.Content.ReadAsStringAsync());
内容的提问来源于stack exchange,提问作者Kresten
相关产品推荐
相关产品推荐

