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

C#中HttpListener忽略请求#后内容,如何获取OAuth的access_token?

如何在C# HttpListener中获取OAuth隐式流的access_token(Hash片段)?

我是C#新手,正在搭建处理StackOverflow OAuth隐式流重定向的服务器,构造的重定向URL是localhost:8080/#access_token=12345。

我写了一段简单的服务器代码来处理请求并保存access_token:

public class ImplicitFlowHttpServer
{
    public int Port = 8080;
    private HttpListener _listener;

    public void Start()
    {
        var prefix = $"http://*:8080/";
        _listener = new HttpListener();
        _listener.Prefixes.Add(prefix);
        _listener.Start();
        _listener.BeginGetContext(new AsyncCallback(ListenerCallback), _listener);
    }

    private async void ListenerCallback(IAsyncResult result)
    {
        if (_listener.IsListening)
        {
            string oauthCode;
            var context = _listener.EndGetContext(result);
            var request = context.Request;
            
            // 发送响应的代码,和问题无关
            HttpListenerResponse response = context.Response;
            byte[] page = Encoding.ASCII.GetBytes(
                "You can close this browser tab now.");

            response.ContentLength64 = page.Length;
            Stream output = response.OutputStream;
            output.Write(page, 0, page.Length);
            output.Close();

            // 调试输出
            Console.WriteLine($"应该收到请求:localhost:8080/#access_token=<token>");
            Console.WriteLine($"怎么才能拿到access_token?");
            Console.WriteLine($"request.Url: {request.Url}");
            Console.WriteLine($"request.Url.Fragment: {request.Url.Fragment}");
            Console.WriteLine($"request.RawUrl: {request.RawUrl}");
            Console.WriteLine($"request.Url.AbsoluteUri: {request.Url.AbsoluteUri}");
            Console.WriteLine($"request.Url.OriginalString: {request.Url.OriginalString}");

            // 这里本来是解析access_token的代码,但现在拿不到内容
            // 这里是停止服务器的代码
        }
    }
}

我原本期望在request对象里找到#后面的内容,但发现这部分完全被清空了。哪怕直接用curl测试curl "localhost:8080/?q=123&hello=world\#access_token=123456",请求对象的相关属性也不包含Fragment部分:

request.Url: http://localhost:8080/?q=123&hello=world%5C
request.Url.Fragment:
request.RawUrl: /?q=123&hello=world\
request.Url.AbsoluteUri: http://localhost:8080/?q=123&hello=world%5C
request.Url.OriginalString: http://localhost:8080/?q=123&hello=world%5C

因为StackOverflow的access_token只能放在Hash里,请问我该怎么从重定向中获取access_token?


解决方法

这不是C#或者HttpListener的问题,而是HTTP协议的特性:浏览器在发送请求时,不会把URL的Hash(#后面的部分)发送给服务器。Hash部分只在浏览器端可见,用来做页面内锚点或者前端路由,不会被包含在HTTP请求头里。

所以要获取Hash里的access_token,必须通过前端JavaScript来处理,再把数据传给服务器:

  1. 修改服务器返回的响应页面,加入JS代码读取URL的Hash
  2. 把Hash中的access_token通过AJAX POST或者表单提交回服务器
  3. 服务器接收并保存这个token

修改后的ListenerCallback中响应部分的代码示例:

// 修改响应内容,加入处理Hash的JS代码
string responseHtml = @"
<!DOCTYPE html>
<html>
<head>
    <script>
        // 读取URL Hash部分
        const hash = window.location.hash.substring(1); // 去掉#
        const params = new URLSearchParams(hash);
        const accessToken = params.get('access_token');
        
        if (accessToken) {
            // 通过AJAX把token传给服务器
            fetch('/save-token', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: `access_token=${accessToken}`
            }).then(() => {
                document.body.innerHTML = 'Token已保存,可以关闭此标签页。';
            });
        } else {
            document.body.innerHTML = '未获取到Token,可以关闭此标签页。';
        }
    </script>
</head>
<body>正在处理Token...</body>
</html>";

byte[] page = Encoding.UTF8.GetBytes(responseHtml);
response.ContentLength64 = page.Length;
response.ContentType = "text/html; charset=utf-8";
Stream output = response.OutputStream;
output.Write(page, 0, page.Length);
output.Close();

然后需要在服务器中添加处理/save-token路径的逻辑,接收POST过来的access_token:

// 在ListenerCallback中添加判断,处理/save-token请求
if (request.Url.AbsolutePath == "/save-token" && request.HttpMethod == "POST")
{
    using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
    {
        string body = reader.ReadToEnd();
        var paramsDict = new Dictionary<string, string>();
        foreach (var pair in body.Split('&'))
        {
            var keyValue = pair.Split('=');
            if (keyValue.Length == 2)
            {
                paramsDict[keyValue[0]] = Uri.UnescapeDataString(keyValue[1]);
            }
        }
        if (paramsDict.TryGetValue("access_token", out string token))
        {
            Console.WriteLine($"获取到的access_token:{token}");
            // 这里添加保存token的逻辑
        }
    }
    // 返回成功响应
    response.StatusCode = 200;
    byte[] successBytes = Encoding.UTF8.GetBytes("OK");
    response.ContentLength64 = successBytes.Length;
    Stream successOutput = response.OutputStream;
    successOutput.Write(successBytes, 0, successBytes.Length);
    successOutput.Close();
    return; // 跳过后续的调试输出逻辑
}

这样就能正常获取到StackOverflow OAuth隐式流返回的access_token了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:21:35