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

如何在C#中用FormUrlEncodedContent发送HTTP请求实现网站登录

解决Lectio.dk登录POST请求失败的问题

看起来你遇到的核心问题是把请求头参数错误地放到了表单数据里,而且缺少ASP.NET页面必需的隐藏验证字段。我来一步步帮你修正:

首先,明确两个关键错误

  1. 请求头和表单数据混放:你把user-agent、accept这些请求头字段加到了FormUrlEncodedContent里,这些应该设置在HttpClient.DefaultRequestHeaders中,而不是作为表单参数提交。
  2. 缺少ASP.NET页面的隐藏验证字段:Lectio的登录页面是ASP.NET写的,这类页面会生成__VIEWSTATE、__VIEWSTATEGENERATOR、__EVENTVALIDATION这些隐藏字段,POST请求必须带上这些值,否则服务器会拒绝你的请求。你需要先GET登录页面,解析HTML获取这些字段的值。

修正后的完整代码示例

我用HtmlAgilityPack来解析HTML(需要先通过NuGet安装这个包),代码改成异步方式避免死锁:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using HtmlAgilityPack;

class LectioLoginExample
{
    static async Task Main(string[] args)
    {
        var cookieContainer = new CookieContainer();
        using var handler = new HttpClientHandler { CookieContainer = cookieContainer };
        using var client = new HttpClient(handler);

        // 1. 设置请求头(这些是浏览器发送的请求头,不是表单数据)
        client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");
        client.DefaultRequestHeaders.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
        client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-GB,en-AS;q=0.9,en-DK;q=0.8,en;q=0.7,da-DK;q=0.6,da;q=0.5,en-US;q=0.4");
        client.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate, br");

        // 2. 先GET登录页面,获取隐藏字段和初始Cookie
        var loginPageUrl = "https://www.lectio.dk/lectio/31/login.aspx";
        var loginPageResponse = await client.GetAsync(loginPageUrl);
        loginPageResponse.EnsureSuccessStatusCode();
        var loginPageHtml = await loginPageResponse.Content.ReadAsStringAsync();

        // 3. 解析HTML获取ASP.NET隐藏字段
        var htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(loginPageHtml);

        string viewState = htmlDoc.DocumentNode.SelectSingleNode("//input[@name='__VIEWSTATE']")?.GetAttributeValue("value", "");
        string viewStateGenerator = htmlDoc.DocumentNode.SelectSingleNode("//input[@name='__VIEWSTATEGENERATOR']")?.GetAttributeValue("value", "");
        string eventValidation = htmlDoc.DocumentNode.SelectSingleNode("//input[@name='__EVENTVALIDATION']")?.GetAttributeValue("value", "");

        if (string.IsNullOrEmpty(viewState) || string.IsNullOrEmpty(viewStateGenerator) || string.IsNullOrEmpty(eventValidation))
        {
            Console.WriteLine("无法获取必要的隐藏验证字段");
            return;
        }

        // 4. 构造正确的表单数据
        var formData = new Dictionary<string, string>
        {
            {"__VIEWSTATE", viewState},
            {"__VIEWSTATEGENERATOR", viewStateGenerator},
            {"__EVENTVALIDATION", eventValidation},
            {"m$Content$username2", "你的用户名"},
            {"m$Content$passwordHidden", "你的密码"}, // 注意:如果浏览器里是m$Content$password,就改成这个
            {"__EVENTTARGET", "m$Content$submitbtn2"},
            {"__EVENTARGUMENT", ""}, // 这个字段通常也需要,浏览器里会带空值
            {"m$Content$rememberme$checkbox", "on"} // 如果需要记住登录,可以加上,否则去掉
        };

        // 5. 发送POST请求
        var content = new FormUrlEncodedContent(formData);
        var postResponse = await client.PostAsync(loginPageUrl, content);
        postResponse.EnsureSuccessStatusCode();

        // 6. 验证登录是否成功:可以检查响应内容是否包含登录后的页面元素,比如用户名或仪表盘
        var responseHtml = await postResponse.Content.ReadAsStringAsync();
        if (responseHtml.Contains("Velkommen") || responseHtml.Contains("Dashboard"))
        {
            Console.WriteLine("登录成功!");
            // 现在CookieContainer里已经保存了登录后的Cookie,可以继续爬取其他页面
        }
        else
        {
            Console.WriteLine("登录失败,请检查用户名密码或表单参数");
            // 可以输出响应HTML查看错误信息
            // Console.WriteLine(responseHtml);
        }
    }
}

额外注意事项

  • 安装HtmlAgilityPack:在NuGet包管理器中搜索并安装HtmlAgilityPack,用来解析HTML获取隐藏字段。
  • 表单参数验证:打开Chrome的网络面板,查看POST请求的Form Data,确保所有字段都包含在formData字典里,比如有些页面可能有__LASTFOCUS字段,也需要加上。
  • 异步调用:避免使用response.Result,改用await,否则可能导致UI线程死锁(特别是在桌面应用中)。
  • 密码字段名:确认浏览器里的密码字段是m$Content$passwordHidden还是m$Content$password,有时候网站会用隐藏字段存储加密后的密码,但通常直接提交明文密码到对应字段即可。
  • CookieContainer:你的思路是对的,CookieContainer会自动保存登录后的Session Cookie,后续请求用同一个HttpClient即可保持登录状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:40:25