我在调试Twitter的REST API v1.1的实现时遇到了问题

时间:2014-01-24 01:37:04

标签: c# twitter utf-8 oauth webclient

作为项目要求的一部分,OAuth从头开始实施,没有任何预制库。 OAuth部分似乎工作正常(我们已经根据twitter的OAuth工具检查了大量签名结果),但请求定期失败401 error

由于OAuth至少似乎返回了一个有效的Authorization标头,我能想到的唯一可能导致问题的是在生成标头后实际编码和发送消息。

这是我们发出请求的地方:

private string Tweet(string tweetTxt)
{
    const string method = "POST";
    const string target = "https://api.twitter.com/1.1/statuses/update.json";

    NameValueCollection data = new NameValueCollection { { "status", tweetTxt } };
    NameValueCollection head = new NameValueCollection
                           {
                               {"user-agent", "AgentX"},
                               {"Authorization", oauth.GetHttpHeader(method, target, null, data)}
                           };
    // example OAuth header return value (wrapped for readability):
    // OAuth oauth_consumer_key="E6wtzO5DRloPq8Ba17osQ", 
    //       oauth_nonce="bfdc54f8b264e4d3d2595266b46d6118",
    //       oauth_signature="8%2B4vXDHYJ3jiWuDvGB6VBKwfWkg%3D", 
    //       oauth_signature_method="HMAC-SHA1", 
    //       oauth_timestamp="1390526341", 
    //       oauth_token="320761140-hJzRG2P9aVtySOS5wLphS4EADm7RfY6NRmZKbvA3", 
    //       oauth_version="1.0"
    using (WebClient client = new WebClient())
    {
        client.Encoding = Encoding.UTF8;
        client.Headers.Add(head);
        try
        {
            client.UploadValues(target, method, data);
            return @"Tweet Succeeded";
        }
        catch (Exception e)
        {
            return @"Tweet Failed";
        }
    }
}

该文本中是否有任何可能导致问题的内容?如果没有,我还有其他地方可以看吗?如果要求特定的内容,我可以发布更多细节。

编辑:我的结论是,问题与某些字符有关。例如,如果推文中包含)(,则会失败。也就是说,带有主题标签的推文(必须进行百分比编码)不会失败,因此它与百分比编码没有直接关系。鉴于Twitter OAuth工具和我们的程序生成完全相同的signature base string和相同的signature,这一切看起来都很奇怪。

编辑2:由于C#没有实现RFC 3986%编码,我们自己实现了。不要特别认为它最终会相关,但是,以防万一,这里是:

private static string PercentEncode(string input)
{
    // List containing all byte values that don't need to be escaped.
    // Source: https://dev.twitter.com/docs/auth/percent-encoding-parameters
    byte[] nonEscaped =
    {
        // Digits
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,

        // Uppercase Letters
        0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
        0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,

        // Lowercase Letters
        0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
        0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,

        // Reserved Characters
        0x2D, 0x2E, 0x5F, 0x7E
    };

    byte[] bytes = Encoding.UTF8.GetBytes(input);
    List<byte> output = new List<byte>();

    foreach (byte b in bytes)
    {
        if (nonEscaped.Contains(b))
        {
            output.Add(b);
        }
        else
        {
            // add percent char
            output.Add(0x25);

            // encode first and last half of byte
            int first = b & 0x0F;
            int last = b >> 4;
            output.Add(Convert.ToByte(last.ToString("X")[0]));
            output.Add(Convert.ToByte(first.ToString("X")[0]));
        }
    }

    return Encoding.UTF8.GetString(output.ToArray());
}

1 个答案:

答案 0 :(得分:0)

我们最终在此代码中发现了两个问题。其中一个导致了所描述的问题。

第一个问题没有引起任何明显的问题,是“Content-Type”标题没有指定UTF-8字符编码。这似乎很小,并没有引起明显的问题。

新标头值如下:

NameValueCollection head = new NameValueCollection
                       {
                           {"Authorization", oauth.GetHttpHeader(method, target, null, data)},
                           {"Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"},
                           {"User-Agent", "AgentX"}
                       };

更重要的问题是允许WebClient使用默认的百分比编码而不是RFC 3986对邮件正文进行百分比编码。通过从WebClient.UploadValues()更改为WebClient.UploadData()并手动创建要发送的字节,我们看到的问题已得到解决。