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






4.55/5 (7投票s)
在接下来的文章中,我希望做的是使用 .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 库
- GReader:http://greader.codeplex.com/
- ngooglereader:http://code.google.com/p/ngooglereader/
这些库的问题在于身份验证无法正常工作(不再)。Google 更改了身份验证的方式。更多信息请参见http://groups.google.com/group/fougrapi/browse_thread/thread/e331f37f7f126c00
此解决方案
- 使用 ClientLogin 身份验证
- 使用 .NET 4.0 Client Profile
GoogleSession
类满足您的所有需求
使用 ClientLogin 进行身份验证
如果要进行身份验证,需要进行一些操作。xandy 在 StackOverFlow 上发表的帖子解释了这个过程(第 1 到 3 点)
- 将登录凭据发布到 https://www.google.com/accounts/ClientLogin。
- 作为回报,如果登录正确,将传递三个令牌:a. SID b. LSID c. Auth
- 将 Auth 保存在应用程序中的某个位置。忘记 SID 和 LSID(我猜他们可能会在以后删除它们)
- 在每个请求中,在标头中添加以下内容: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
)。 - 显示这些文章
结果
暂时不用担心 Google Reader URL。在下一篇文章中,我将讨论创建一个与 Google Reader 通信的类以及 URL 的工作方式。
享受吧...
历史
- 2010 年 7 月 6 日:初始帖子