QuickBooks在线查询过滤器每次都返回401

时间:2013-02-04 20:30:08

标签: quickbooks intuit-partner-platform quickbooks-online

我已成功使用POST和Content-Type application / xml

创建对象

我也成功使用Content-Type application / x-www-form-urlencoded查询了一个空白请求体,它根据我指定的URI返回所有对象类型。

我也可以在请求正文中使用类似PageNum = 1& ResultsPerPage = 1的内容,并且我已经想出如何将其合并到签名中,以便获得有效的响应。

然而,无论我如何格式化,当我尝试使用过滤器时,我都无法得到除401响应之外的任何东西(像Filter = FAMILYNAME:EQUALS:Doe这样的基本内容)。我已经阅读了关于如何使用[RFC3986]百分比编码转义所有参数名称和值的OAuth Core 1.0 Revision A规范。但是我觉得我错过了一个步骤或格式错误。我在通过Intuit的论坛搜索时看到的信息不一致,究竟是什么格式正确。

对此的任何帮助将不胜感激。我现在一直在努力工作一周。

尝试使用过滤器时得到的响应是: HTTP状态401 - 消息=验证OAuth的异常;错误码= 003200;的StatusCode = 401

---- ----更新

当我尝试使用新的IPP开发人员工具 - IPP API Explorer时,我看到同样的错误。我正在使用IDS V2 QBO API Explorer。我能够使用该工具检索所有Post并且响应显示我的所有客户,但当我尝试使用过滤器时,我得到: 服务器错误 401 - 未授权:由于凭据无效,访问被拒绝。 您无权使用您提供的凭据查看此目录或页面。

任何想法?如果我从API Explorer工具中得到相同的错误,那么我认为问题完全是另一回事。

----最终更新----

我终于有了过滤器的成功,我相信我已经弄明白我的问题是什么。我总是怀疑我能够通过像“PageNum = 1& ResultsPerPage = 1”这样的分页来查询,但却无法获得类似“Filter = FAMILYNAME:EQUALS:Doe”的内容。我怀疑问题在于过滤格式中的空白区域。是什么让我无法跟踪此问题,因为我无法让过滤器在IDS V2 QBO API Explorer中运行。这让我怀疑还有其他事情发生了。我决定一起忽略API Explorer,并专注于为什么我可以让它以单向工作而不是另一种方式工作。

我认为我的问题归结为签名中过滤器值的编码不正确。这解释了我得到的401无效签名错误。

标准化后,“过滤器=名称:EQUALS:Doe”变为“过滤器=名称%20%3AEQUALS%20%3ADoe”。

百分比编码应该给出“Filter%3DName%2520%253AEQUALS%2520%253ADoe”。

实质上,你必须对空格和冒号进行“双重”编码,而不是等号。我尝试了许多编码的排列,但我相信我的错误是我不是“双重”编码,或者当我进行双重编码时,我包含了'='符号。无论哪种方式打破你的签名。感谢大家的投入。

4 个答案:

答案 0 :(得分:2)

我认为我的问题归结为签名中过滤器值的编码不正确。这解释了我得到的401无效签名错误。

我使用在线工具指导我完成正确签署Oauth请求的步骤。在完成这些步骤的过程中,我意识到我的问题在于您规范化请求参数然后对它们进行百分比编码的步骤。我在规范化步骤中包含过滤器的'=',这会破坏您的签名。我使用的工具可以在以下网址找到:

http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/

感谢大家的投入。

答案 1 :(得分:1)

您是否在API Explorer中获得了具有相同请求的401?

http://ippblog.intuit.com/blog/2013/01/new-ipp-developer-tool-api-explorer.html

另外,您使用静态基本URL还是在运行时检索它?

https://ipp.developer.intuit.com/0010_Intuit_Partner_Platform/0050_Data_Services/0400_QuickBooks_Online/0100_Calling_Data_Services/0010_Getting_the_Base_URL

如果您使用的是静态基本URL,请尝试切换到运行时基本URL,以查看是否仍然出现错误。

答案 2 :(得分:1)

peterl在这里回答了我的一个问题,也可以回答你的问题。当他们应该进入标题时,我一直试图将过滤器放入体内。以下是peterl的代码示例,用于获取特定客户的所有未付发票(未结余额大于0.00)。

http://pastebin.com/raw.php?i=7VUB6whp

public List<Intuit.Ipp.Data.Qbo.Invoice> GetQboUnpaidInvoices(DataServices dataServices, int startPage, int resultsPerPage,  IdType CustomerId)
{
    StringBuilder requestXML = new StringBuilder();
    StringBuilder responseXML = new StringBuilder();

    var requestBody = String.Format("PageNum={0}&ResultsPerPage={1}&Filter=OpenBalance :GreaterThan: 0.00 :AND: CustomerId :EQUALS: {2}", startPage, resultsPerPage, CustomerId.Value);

    HttpWebRequest httpWebRequest = WebRequest.Create(dataServices.ServiceContext.BaseUrl + "invoices/v2/" + dataServices.ServiceContext.RealmId) as HttpWebRequest;
    httpWebRequest.Method = "POST";
    httpWebRequest.ContentType = "application/x-www-form-urlencoded";
    httpWebRequest.Headers.Add("Authorization", GetDevDefinedOAuthHeader(httpWebRequest, requestBody));
    requestXML.Append(requestBody);
    UTF8Encoding encoding = new UTF8Encoding();
    byte[] content = encoding.GetBytes(requestXML.ToString());
    using (var stream = httpWebRequest.GetRequestStream())
    {
        stream.Write(content, 0, content.Length);
    }
    HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
    using (Stream data = httpWebResponse.GetResponseStream())
    {
        Intuit.Ipp.Data.Qbo.SearchResults searchResults = (Intuit.Ipp.Data.Qbo.SearchResults)dataServices.ServiceContext.Serializer.Deserialize<Intuit.Ipp.Data.Qbo.SearchResults>(new StreamReader(data).ReadToEnd());
        return ((Intuit.Ipp.Data.Qbo.Invoices)searchResults.CdmCollections).Invoice.ToList();
    }

}

protected string GetDevDefinedOAuthHeader(HttpWebRequest webRequest, string requestBody)
{

    OAuthConsumerContext consumerContext = new OAuthConsumerContext
    {
        ConsumerKey = consumerKey,
        ConsumerSecret = consumerSecret,
        SignatureMethod = SignatureMethod.HmacSha1,
        UseHeaderForOAuthParameters = true

    };

    consumerContext.UseHeaderForOAuthParameters = true;

    //URIs not used - we already have Oauth tokens
    OAuthSession oSession = new OAuthSession(consumerContext, "https://www.example.com",
                            "https://www.example.com",
                            "https://www.example.com");


    oSession.AccessToken = new TokenBase
    {
        Token = accessToken,
        ConsumerKey = consumerKey,
        TokenSecret = accessTokenSecret
    };

    IConsumerRequest consumerRequest = oSession.Request();
    consumerRequest = ConsumerRequestExtensions.ForMethod(consumerRequest, webRequest.Method);
    consumerRequest = ConsumerRequestExtensions.ForUri(consumerRequest, webRequest.RequestUri);
    if (webRequest.Headers.Count > 0)
    {
        ConsumerRequestExtensions.AlterContext(consumerRequest, context => context.Headers = webRequest.Headers);
        if (webRequest.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded")
        {
            Dictionary<string, string> formParameters = new Dictionary<string, string>();
            foreach (string formParameter in requestBody.Split('&'))
            {
                formParameters.Add(formParameter.Split('=')[0], formParameter.Split('=')[1]);
            }
            consumerRequest = consumerRequest.WithFormParameters(formParameters);
        }
    }

    consumerRequest = consumerRequest.SignWithToken();
    return consumerRequest.Context.GenerateOAuthParametersForHeader();
}

您还可以在StackOverflow上看到我原来的问题:Query for All Invoices With Open Balances using QuickBooks Online (QBO) Intuit Partner Platform (IPP) DevKit

答案 3 :(得分:0)

我在QuickBooks OAuth请求中有written about my experience个转义字符。