Intuit IPP Rest API query string escape

2019-05-20 08:45发布

So, for the Intuit IPP Rest API,

Say if i want to query a customer who's name is ABC, i can use a http get request like this

https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'ABC'

&3D is a url escape of '=', this works without any problem.

Now if the customer's name is A&B, I tried the query string like this

select Id from Customer where FullyQualifiedName='A&B'

After the url encoding, it looks like this

https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'A%26B'

It will fail.

Any Idea?

Update

The above urls i copied from the IPP's API explorer.

Here is the code, I am using DevDefined.OAuth

        IConsumerRequest conReq = _oSession.Request();
        conReq = conReq.Get();
        conReq.AcceptsType = "application/xml";
        //conReq = conReq.ForUrl(string.Format(@"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, @"select * from Customer where DisplayName='ABC'"));   if use this line, it works fine
        conReq = conReq.ForUrl(string.Format(@"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, @"select * from Customer where DisplayName='A&B'"));
        try
        {                
            string str = conReq.ReadBody();

        catch (Exception ex)
        {
            //ex.Message
        }

the returned xml data like this

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T06:24:12.408-07:00">
  <Fault type="ValidationFault">
    <Error code="4000">
      <Message>Error parsing query</Message>
      <Detail>QueryParserError: Invalid content. Lexical error at line 1, column 44.  Encountered: &lt;EOF&gt; after : &quot;\'A&quot;</Detail>
    </Error>
  </Fault>
</IntuitResponse>

I am not 100% sure, yesterday when i test, it actually return something says the oauth failed. But this is what I got this morning.

Actually, you can try it within IPP's API explorer, it gives the same result.

The devdefined's code for ForUrl

public static IConsumerRequest ForUrl(this IConsumerRequest request, string url)
{
    request.Context.RawUri = new Uri(url);
    return request;
}

That will encode the url as

https://qb.sbfinance.intuit.com/v3/company/1122502005/query?query=select%20*%20from%20Customer%20where%20DisplayName='A&B'

Ok, finally, found the issue:

The real issue is Uri(url) won't escape the & in 'A&B' because it doesn't know if it is a url & or part of the data, So i changed the following line

conReq = conReq.ForUrl(string.Format(@"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, @"select * from Customer where DisplayName='A&B'"));

as

conReq = conReq.ForUrl(string.Format(@"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, Uri.EscapeDataString(@"select * from Customer where DisplayName='A&B'")));

use Uri.EscapeDataString to escape the data query string first.

2条回答
我命由我不由天
2楼-- · 2019-05-20 09:36

If you create a customer with name 'A&B', then V3 service returns that customer object like below -

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T01:54:46.834-07:00">
    <Customer domain="QBO" sparse="false">
        <Id>10</Id>
        <SyncToken>0</SyncToken>
        <MetaData>
            <CreateTime>2014-03-20T01:54:46-07:00</CreateTime>
            <LastUpdatedTime>2014-03-20T01:54:47-07:00</LastUpdatedTime>
        </MetaData>
        <FullyQualifiedName>A&amp;B</FullyQualifiedName>
        <DisplayName>A&amp;B</DisplayName>
        <PrintOnCheckName>A&amp;B</PrintOnCheckName>
        <Active>true</Active>
        <Taxable>true</Taxable>
        <Job>false</Job>
        <BillWithParent>false</BillWithParent>
        <Balance>100.00</Balance>
        <BalanceWithJobs>100.00</BalanceWithJobs>
        <PreferredDeliveryMethod>Email</PreferredDeliveryMethod>
    </Customer>
</IntuitResponse>

To retrieve this object by name, you need to use the following query

SELECT * FROM Customer WHERE DisplayName = 'A&B'

But it needs to be url encoded like following

SELECT+*+FROM+Customer+WHERE+DisplayName+%3D+%27A%26B%27

Java code to achieve this -

Customer customer = GenerateQuery.createQueryEntity(Customer.class);
String query = select($(customer)).where($(customer.getDisplayName()).eq("A&B")).generate();
// Query output - SELECT * FROM Customer WHERE DisplayName = 'A&B'
String encodedUrl = URLEncoder.encode(query, "UTF-8");

It works perfectly. (Devkit handles all these pretty well )

Hope this answers your qts.

Thanks

查看更多
Fickle 薄情
3楼-- · 2019-05-20 09:50

Please see an example here to escape special chars-

using Intuit.Ipp.Core;
using Intuit.Ipp.Data;
using Intuit.Ipp.LinqExtender;
using Intuit.Ipp.QueryFilter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


static class SampleCalls
{
public static Customer QueryCustomerByDisplayName(ServiceContext context, string     displayName)     {
displayName = displayName.Replace("'", "\\'"); //Escape special characters
QueryService<Customer> customerQueryService = new QueryService<Customer>(context);
return customerQueryService.Where(m => m.DisplayName == displayName).FirstOrDefault();
}
}
查看更多
登录 后发表回答