C#调用WikiMedia API登录失败:会话超时错误排查求助
问题:MediaWiki API登录失败,提示“会话超时”但实际并非如此
我正在编写一个C#脚本,用于登录私有Wiki以进行编辑操作。以下是实现代码:
public static class WikiAccess { static string apiUrl = "https://en.wikipedia.org/w/api.php"; // 使用可用的机器人用户名和密码调用此方法 public static async Task<bool> LoginToWikiAsync(string userName, string password) { // 创建获取登录令牌的请求参数 var tokenParameters = new Dictionary<string, string> { { "action", "query" }, { "format", "json" }, { "meta", "tokens" }, { "formatversion", "2" }, { "type", "login" }, }; // 发送登录令牌请求 var tokenResponse = await SendMessageToWikiGet(tokenParameters); JObject tokenJsonResponse = ParseString(tokenResponse); // 获取登录令牌 string loginToken = tokenJsonResponse["query"]["tokens"]["logintoken"].Value<string>(); var loginParameters = new Dictionary<string, string> { { "action", "login" }, { "format", "json" }, { "lgname", userName }, { "lgpassword", password }, { "lgtoken", loginToken }, { "formatversion", "2" }, }; // 发送登录请求 var loginResponse = await SendMessageToWikiPost(loginParameters); JObject loginJsonResponse = ParseString(loginResponse); Debug.Log("Login Result: " + loginJsonResponse); /* 登录结果:{ "login": { "result": "Failed", "reason": "Unable to continue login. Your session most likely timed out." } } */ string loginResult = loginJsonResponse["login"]["result"].Value<string>(); // 登录结果返回"Failed" return loginResult == "Success"; } static JObject ParseString(string value) { try { return JObject.Parse(value); } catch (Exception ex) { Debug.LogError(value); throw ex; } } public static Task<string> SendMessageToWikiGet(Dictionary<string, string> parameters) { // 构建查询字符串并通过Get发送请求到指定URL string url = apiUrl + "?" + BuildQueryString(parameters); Debug.Log(url); return SendMessageToUrlGet(url); } public static Task<string> SendMessageToWikiPost(Dictionary<string, string> parameters) { // 构建查询字符串并通过Post发送请求到指定URL return SendMessageToUrlPost(apiUrl, BuildQueryString(parameters)); } static WebClient client; public async static Task<string> SendMessageToUrlPost(string url, string parameters) { client ??= new(); client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; var response = await client.UploadStringTaskAsync(url, parameters); return response; } public async static Task<string> SendMessageToUrlGet(string url) { client ??= new(); var tokenResponse = await client.DownloadStringTaskAsync(url); return tokenResponse; } static string BuildQueryString(Dictionary<string, string> parameters) { StringBuilder queryBuilder = new StringBuilder(); foreach (var parameter in parameters) { queryBuilder.Append($"{Uri.EscapeDataString(parameter.Key)}={Uri.EscapeDataString(parameter.Value)}&"); } string returnValue = queryBuilder.ToString().TrimEnd('&'); //Debug.Log(returnValue); return returnValue; } }
发送登录令牌请求时能成功获取到令牌,但使用该令牌发起登录请求时,返回结果为Failed,错误原因显示:
Unable to continue login. Your session most likely timed out.
无论是维基百科公开API还是私有Wiki,都存在同样问题。而且无论输入正确还是错误的用户名和密码,都会收到该提示,判断并非真的会话超时,请问遗漏了什么?
期望响应:
{ "login": { "result": "Success", "lguserid": 1, "lgusername": "UserName" } }
实际响应:
{ "login": { "result": "Failed", "reason": "Unable to continue login. Your session most likely timed out." } }
解决方案
问题出在会话Cookie的处理上:MediaWiki的登录流程需要保持同一个会话上下文,也就是获取登录令牌时服务器返回的Cookie,必须在后续的登录请求中一并发送。原生WebClient的Cookie处理在异步跨请求场景下可能无法正确保留会话,导致服务器认为是新会话,从而返回“超时”错误。
修复步骤
- 扩展WebClient以支持显式Cookie容器
原生WebClient未公开CookieContainer,需要自定义子类来管理会话Cookie:
public class CookieAwareWebClient : WebClient { private CookieContainer _cookieContainer = new CookieContainer(); protected override WebRequest GetWebRequest(Uri address) { WebRequest request = base.GetWebRequest(address); if (request is HttpWebRequest httpRequest) { httpRequest.CookieContainer = _cookieContainer; } return request; } }
- 替换原代码中的WebClient实例
把所有WebClient替换为自定义的CookieAwareWebClient,确保会话Cookie在两次请求间正确传递:
static CookieAwareWebClient client; public async static Task<string> SendMessageToUrlPost(string url, string parameters) { client ??= new CookieAwareWebClient(); client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; var response = await client.UploadStringTaskAsync(url, parameters); return response; } public async static Task<string> SendMessageToUrlGet(string url) { client ??= new CookieAwareWebClient(); var tokenResponse = await client.DownloadStringTaskAsync(url); return tokenResponse; }
- 额外检查项
- 若私有Wiki版本较旧,尝试移除
formatversion=2参数,改用MediaWiki默认格式 - 维基百科等公开Wiki的机器人账号,需使用Bot专用密码(而非用户普通密码),并确保账号已启用机器人权限
修复原理
显式的CookieContainer会完整保留获取令牌请求时服务器返回的会话标识,登录请求携带该Cookie后,服务器就能识别这是同一个会话的连续操作,从而完成登录流程。
内容的提问来源于stack exchange,提问作者Skie
相关产品推荐
相关产品推荐

