65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 .NET 访问 Google (Reader):第一部分 - 身份验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (7投票s)

2010年7月6日

Ms-PL

3分钟阅读

viewsIcon

32172

downloadIcon

567

在接下来的文章中,我希望做的是使用 .NET 连接并访问 Google 服务(重点是 Google Reader)。

引言

几个星期以来,我越来越喜欢 Google Reader。这个出色的在线 RSS 阅读器支持星标项目、扩展搜索等等,满足您对优秀 RSS 阅读器的所有期望。在接下来的文章中,我希望做的是连接并访问 Google 服务。

在开始之前,我需要指出的是,Google 已经编写了一个现成的 .NET API,称为 Google Data Protocol。这个库支持许多 Google 服务,并且有很好的文档。

您可以在这里找到它:http://code.google.com/apis/gdata/

我个人认为的缺点

  • 不支持 .NET Client Profile
  • 需要引用外部程序集
  • 没有对 Google Reader 的支持/文档?

同样重要的是要知道,还有其他可用于访问 Google Reader 的 .NET 库

这些库的问题在于身份验证无法正常工作(不再)。Google 更改了身份验证的方式。更多信息请参见http://groups.google.com/group/fougrapi/browse_thread/thread/e331f37f7f126c00

此解决方案

  • 使用 ClientLogin 身份验证
  • 使用 .NET 4.0 Client Profile
  • GoogleSession 类满足您的所有需求

使用 ClientLogin 进行身份验证

如果要进行身份验证,需要进行一些操作。xandy 在 StackOverFlow 上发表的帖子解释了这个过程(第 1 到 3 点)

  1. 将登录凭据发布到 https://www.google.com/accounts/ClientLogin
  2. 作为回报,如果登录正确,将传递三个令牌:a. SID b. LSID c. Auth
  3. 将 Auth 保存在应用程序中的某个位置。忘记 SID 和 LSID(我猜他们可能会在以后删除它们)
  4. 在每个请求中,在标头中添加以下内容:headername
    Authorization value: GoogleLogin auth={Auth string} 例如(在 Java 中)

以下类执行所有这些操作,并在成功登录后返回 Auth 令牌

public static class ClientLogin
{
    /// <summary>
    /// Client login url where we'll post login data to.
    /// </summary>
    private static string clientLoginUrl = 
        @"https://www.google.com/accounts/ClientLogin";
        
    /// <summary>
    /// Data to be sent with the post request.
    /// </summary>
    private static string postData = 
        @"service={0}&continue=http://www.google.com/&Email={1}&Passwd={2}&source={3}";
        
    /// <summary>
    /// Get the Auth token you get after a successful login. 
    /// You'll need to reuse this token in the header of each new request you make.
    /// </summary>
    /// <param name="service"></param>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <param name="source"></param>
    /// <returns></returns>
    public static string GetAuthToken(
        string service, string username, string password, string source)
    {
        // Get the response that needs to be parsed.
        string response = PostRequest(service, username, password, source);
        // Get auth token.
        string auth = ParseAuthToken(response);
        return auth;
    }
    
    /// <summary>
    /// Parse the Auth token from the response.
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    private static string ParseAuthToken(string response)
    {            
        // Get the auth token.
        string auth = "";
        try
        {
            auth = new Regex(@"Auth=(?<auth>\S+)").Match(response).Result("${auth}");
        }
        catch (Exception ex)
        {
            throw new AuthTokenException(ex.Message);
        }
        // Validate token.
        if (string.IsNullOrEmpty(auth))
        {
            throw new AuthTokenException("Missing or invalid 'Auth' token.");
        }
        // Use this token in the header of each new request.
        return auth;
    }
    
    /// <summary>
    /// Create a post request with all the login data. This will return something like:
    /// 
    /// SID=AQAAAH1234
    /// LSID=AQAAAH8AA5678
    /// Auth=AQAAAIAAAAB910
    /// 
    /// And we need the Auth token for each subsequent request.
    /// </summary>
    /// <param name="service"></param>
    /// <param name="email"></param>
    /// <param name="password"></param>
    /// <param name="source"></param>
    /// <returns></returns>
    private static string PostRequest(
        string service, string email, string password, string source)
    {
        // Get the current post data and encode.
        ASCIIEncoding ascii = new ASCIIEncoding();
        byte[] encodedPostData = ascii.GetBytes(
            String.Format(postData, service, email, password, source));
        
        // Prepare request.
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(clientLoginUrl);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = encodedPostData.Length;
        // Write login info to the request.
        using (Stream newStream = request.GetRequestStream())
            newStream.Write(encodedPostData, 0, encodedPostData.Length);
        // Get the response that will contain the Auth token.
        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
        }
        catch (WebException ex)
        {
            HttpWebResponse faultResponse = ex.Response as HttpWebResponse;
            if (faultResponse != null && 
		faultResponse.StatusCode == HttpStatusCode.Forbidden)
                throw new IncorrectUsernameOrPasswordException(
                    faultResponse.StatusCode, faultResponse.StatusDescription);
            else
                throw;
        }
        // Check for login failed.
        if (response.StatusCode != HttpStatusCode.OK)
            throw new LoginFailedException(
                response.StatusCode, response.StatusDescription);
        // Read.
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            return reader.ReadToEnd();
    }
}

就是这样。如果调用 ClientLogin.GetAuthToken,您将获得可用于每个后续请求的 auth 令牌。
您还将获得正确类型的异常,具体取决于您收到的错误。如果异常未知,则只会重新抛出。

GoogleSession 类

在我们使用 Auth 令牌发出请求之前,我们需要注意一些事项

  • 重用 Auth 令牌
  • 将 Auth 令牌添加到请求标头(StackOverFlow 帖子中的第 4 点)
  • 支持参数(例如前 10 个项目等)
  • 以可重用的方式处理请求(Response 作为流、字符串、feed 等)

这就是我们为什么使用以下类来封装所有这些功能

public class GoogleSession : IDisposable
{ 
    /// <summary>
    /// Auth token.
    /// </summary>
    private string auth;
    
    /// <summary>
    /// Initialize request.
    /// </summary>
    /// <param name="auth"></param>
    public GoogleSession(string auth)
    {
        this.auth = auth;
    }
     
    /// <summary>
    /// Create a google request and get the response.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public WebResponse GetResponse(string url, params GoogleParameter[] parameters)
    {
        // Format the parameters.
        string formattedParameters = string.Empty;
        foreach (var par in parameters)
            formattedParameters += string.Format("{0}={1}&", par.Name, par.Value);
        formattedParameters = formattedParameters.TrimEnd('&');
        // Create a request with or without parameters.
        HttpWebRequest request = null;
        if (formattedParameters.Length > 0)
            request = (HttpWebRequest)WebRequest.Create(string.Format("{0}?{1}", 
                url, formattedParameters));
        else 
            request = (HttpWebRequest)WebRequest.Create(url);
        // Add the authentication header. 
        request.Headers.Add("Authorization", "GoogleLogin auth=" + auth);
        // Get the response, validate and return.
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        if (response == null)
            throw new GoogleResponseNullException();
        else if (response.StatusCode != HttpStatusCode.OK)
            throw new GoogleResponseException(response.StatusCode, 
                response.StatusDescription);
        return response;
    }
    
    /// <summary>
    /// Create a google request and get the response stream.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public Stream GetResponseStream(string url, params GoogleParameter[] parameters)
    {
        return GetResponse(url, parameters).GetResponseStream();
    }
    
    /// <summary>
    /// Create a google request and get the page source.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public string GetSource(string url, params GoogleParameter[] parameters)
    {
        using (StreamReader reader = new StreamReader(
            GetResponseStream(url, parameters)))
        {
            return reader.ReadToEnd();
        }
    }
    
    /// <summary>
    /// Create a google request and get the feed.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public SyndicationFeed GetFeed(string url, params GoogleParameter[] parameters)
    {
        // Load the stream into the reader.
        using (StreamReader reader = new StreamReader(
            GetResponseStream(url, parameters)))
        {
            // Create an xml reader out of the stream reader.
            using (XmlReader xmlReader = XmlReader.Create(reader, 
                new XmlReaderSettings()))
            {
                // Get a syndication feed out of the xml.
                return SyndicationFeed.Load(xmlReader);    
            }                
        }
    }
    
    /// <summary>
    /// Clean up the authentication token.
    /// </summary>
    public void Dispose()
    {
        auth = null;
    }
}      

你可以用它做什么

  • 创建支持参数的经过身份验证的响应
  • 将此响应作为 Stream 获取
  • 将此响应作为 feed 获取
  • 将此响应作为 string (原始源)获取

整合

现在我们可以轻松地进行身份验证和发出请求了,让我们利用它。这是一个命令行应用程序的示例,显示了 Google Reader 中最新的 5 篇文章。

class Program
{
    static void Main(string[] args)
    { 
        // Empty line.
        Console.WriteLine("");
        
        // Get username.
        Console.Write(" Enter your Google username: ");
        string username = Console.ReadLine();
        
        // Get password.
        Console.Write(" Enter your password: ");
        string password = Console.ReadLine();
        
        // Authenticate.
        string auth = ClientLogin.GetAuthToken
		("reader", username, password, "Sandworks.Google.App");
        
        // Query.
        using (GoogleSession session = new GoogleSession(auth))
        {
            var feed = session.GetFeed
	    ("http://www.google.com/reader/atom/user/-/state/com.google/reading-list",
                new GoogleParameter("n", "5"));
                
            // Display.
            Console.WriteLine("");
            Console.WriteLine(" Last 5 articles in Google Reader: ");
            foreach (var item in feed.Items)
            {
                Console.WriteLine("  - " + item.Title.Text);
            }
        }
        
        // Pause.
        Console.ReadLine();
    }
}

它的作用

  • 询问您的 Google 帐户
  • 询问您的密码
  • 使用 ClientLogin 进行身份验证并获取 Auth 令牌
  • 使用 Auth 令牌创建一个新的 GoogleSession
  • 连接到 Google Reader 并获取最新的 5 篇文章(使用 GoogleParameter)。
  • 显示这些文章

结果

GoogleReaderApp.png

暂时不用担心 Google Reader URL。在下一篇文章中,我将讨论创建一个与 Google Reader 通信的类以及 URL 的工作方式。

享受吧...

历史

  • 2010 年 7 月 6 日:初始帖子
© . All rights reserved.