Установите AllowAutoRedirect false в существующий HttpClient только для одного запроса - PullRequest
1 голос
/ 22 марта 2020

Этот ответ на вопрос о том, как заставить HttpClient не следовать перенаправлениям, дает решение, которое необходимо установить при создании фактического клиента:

var handler = new HttpClientHandler { AllowAutoRedirect = false };    
var client = new HttpClient(handler);

Комментарий под ответом мой актуальный вопрос:

Возможно ли сделать это на основе запроса без необходимости двух отдельных экземпляров HttpClient (т.е. один, который разрешает перенаправления, и один, который не делает) ?

У меня есть также конкретная причина c для того, чтобы теперь хотеть отдельных клиентов: я хочу, чтобы клиент сохранил свои куки-файлы из предыдущих запросов. Сначала я пытаюсь выполнить несколько запросов, которые включают действительные перенаправления, но только один последний в цепочке. Я не хочу быть перенаправлением.

Я искал, и просмотрел перегрузки .GetAsync(url, ...), просмотрел свойства и методы HttpClient, но пока не нашел решения.

Возможно ли это?

Ответы [ 2 ]

2 голосов
/ 22 марта 2020

Как вы, вероятно, обнаружили, вам не разрешено изменять конфигурацию HttpClientHandler после того, как был сделан запрос.

Поскольку ваша мотивация для этого состоит в том, чтобы поддерживать куки между запросами Затем я предлагаю что-то более похожее (без обработки исключений / обработки нулевых ссылок):

    static CookieContainer cookieJar = new CookieContainer();

    static async Task<HttpResponseMessage> GetAsync(string url, bool autoRedirect)
    {
        HttpResponseMessage result = null;

        using (var handler = new HttpClientHandler())
        using (var client = new HttpClient(handler))
        {
            handler.AllowAutoRedirect = autoRedirect;
            handler.CookieContainer = cookieJar;

            result = await client.GetAsync(url);

            cookieJar = handler.CookieContainer;
        }

        return result;
    }

Тест:

    static async Task Main(string[] args)
    {
        string url = @"http://stackoverflow.com";

        using (var response = await GetAsync(url, autoRedirect: false))
        {
            Console.WriteLine($"HTTP {(int)response.StatusCode} {response.StatusCode}");
            Console.WriteLine($"{response.Headers}");

            Console.WriteLine("Cookies:");
            Console.WriteLine($"{cookieJar.GetCookieHeader(new Uri(url))}\r\n");
        }

        Console.WriteLine(new string('-', 30));

        using (var response = await GetAsync(url, autoRedirect: true))
        {
            Console.WriteLine($"HTTP {(int)response.StatusCode} {response.StatusCode}");
            Console.WriteLine($"{response.Headers}");

            Console.WriteLine("Cookies:");
            Console.WriteLine($"{cookieJar.GetCookieHeader(new Uri(url))}\r\n");
        }

        Console.ReadLine();
    }
2 голосов
/ 22 марта 2020

Да, вы можете установить свойства HttpClientHandler для каждого запроса, например, так:

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    handler.AllowAutoRedirect = false;
    // do your job
    handler.AllowAutoRedirect = true;
}

Просто убедитесь, что только один поток потребляет HttpClient одновременно, если настройки обработчика клиента отличаются.


Пример (примечание: работает только в тестовой среде)

Пустой удаленный сервер с Node.js runnin на localhost:

const express = require('express')
const app = express()
const cookieParser = require('cookie-parser')
const session = require('express-session')
const port = 3000

app.use(cookieParser());
app.use(session({secret: "super secret"}))

app.get('/set-cookie/:cookieName', (req, res) => {
    const  cookie = Math.random().toString()
    req.session[req.params.cookieName] = cookie
    res.send(cookie)
});

app.get('/ok', (req, res) => res.send('OK!'))

app.get('/redirect-301', (req, res) => {
    res.writeHead(301, {'Location': '/ok'})
    res.end();
})

app.get('/get-cookie/:cookieName', (req, res) => res.send(req.session[req.params.cookieName]))

app.listen(port, () => console.log(`App listening on port ${port}!`))

Тесты

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NUnit.Framework;

public class Tests
{
    private HttpClientHandler handler;
    private HttpClient client;
    private CookieContainer cookieJar = new CookieContainer();
    private string cookieName = "myCookie";
    private string cookieValue;

    [SetUp]
    public void Setup()
    {
        handler = new HttpClientHandler()
        {
            AllowAutoRedirect = true,
            CookieContainer = cookieJar
        };
        client = new HttpClient(handler);
    }

    [Test]
    public async Task Test0()
    {
        using (var response = await client.GetAsync($"http://localhost:3000/set-cookie/{cookieName}"))
        {
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            cookieValue = await response.Content.ReadAsStringAsync();
        }
    }

    [Test]
    public async Task Test1()
    {
        handler.AllowAutoRedirect = true;
        using (var response = await client.GetAsync("http://localhost:3000/redirect-301"))
        {
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.AreEqual(await response.Content.ReadAsStringAsync(), "OK!");
        }
    }

    [Test]
    public async Task Test2()
    {
        handler.AllowAutoRedirect = false;
        using (var response = await client.GetAsync("http://localhost:3000/redirect-301"))
        {
            Assert.AreEqual(HttpStatusCode.MovedPermanently, response.StatusCode);
        }
    }

    [Test]
    public async Task Test3()
    {
        using (var response = await client.GetAsync($"http://localhost:3000/get-cookie/{cookieName}"))
        {
            Assert.AreEqual(await response.Content.ReadAsStringAsync(), cookieValue);
        }
    }
}

Вывод через dotnet test:

Test Run Successful.
Total tests: 4
     Passed: 4
 Total time: 0.9352 Seconds
...