You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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处理在异步跨请求场景下可能无法正确保留会话,导致服务器认为是新会话,从而返回“超时”错误。

修复步骤

  1. 扩展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;
    }
}
  1. 替换原代码中的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;
}
  1. 额外检查项
  • 若私有Wiki版本较旧,尝试移除formatversion=2参数,改用MediaWiki默认格式
  • 维基百科等公开Wiki的机器人账号,需使用Bot专用密码(而非用户普通密码),并确保账号已启用机器人权限

修复原理

显式的CookieContainer会完整保留获取令牌请求时服务器返回的会话标识,登录请求携带该Cookie后,服务器就能识别这是同一个会话的连续操作,从而完成登录流程。


内容的提问来源于stack exchange,提问作者Skie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:44:57