我的代码正在对需要基本身份验证的Web服务URL进行HTTP GET。
我使用HttpClient
并HttpClientHandler
定义了Credentials
属性来实现此功能。
这一切都很完美。除了我的一个用例,我正在进行经过身份验证的GET:
http://somedomain.com
重定向到http://www.somedomain.com
。
似乎HttpClientHandler在重定向期间清除了身份验证标头。我怎么能阻止这个?无论重定向如何,我都希望发送凭据。
这是我的代码:
// prepare the request
var request = new HttpRequestMessage(method, url);
using (var handler = new HttpClientHandler { Credentials = new NetworkCredential(username, password) , PreAuthenticate = true })
using (var client = new HttpClient(handler))
{
// send the request
var response = await client.SendAsync(request);
注意:这是一个相关的问题: Keeping HTTP Basic Authentification alive while being redirected 但由于我使用不同的类来发出请求,因此可能会有更好,更具体的解决方案
答案 0 :(得分:2)
默认的HttpClientHandler使用相同的HttpWebRequest基础结构。而不是将NetworkCredential分配给Credentials属性,而是创建CredentialCache并分配它。
这就是我用来代替AutoRedirect的东西,并且带有一点async / await仙尘,它可能会更漂亮,更可靠。
public class GlobalRedirectHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var tcs = new TaskCompletionSource<HttpResponseMessage>();
base.SendAsync(request, cancellationToken)
.ContinueWith(t => {
HttpResponseMessage response;
try {
response = t.Result;
}
catch (Exception e) {
response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
response.ReasonPhrase = e.Message;
}
if (response.StatusCode == HttpStatusCode.MovedPermanently
|| response.StatusCode == HttpStatusCode.Moved
|| response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther
|| response.StatusCode == HttpStatusCode.RedirectKeepVerb
|| response.StatusCode == HttpStatusCode.TemporaryRedirect
|| (int)response.StatusCode == 308)
{
var newRequest = CopyRequest(response.RequestMessage);
if (response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther)
{
newRequest.Content = null;
newRequest.Method = HttpMethod.Get;
}
newRequest.RequestUri = response.Headers.Location;
base.SendAsync(newRequest, cancellationToken)
.ContinueWith(t2 => tcs.SetResult(t2.Result));
}
else {
tcs.SetResult(response);
}
});
return tcs.Task;
}
private static HttpRequestMessage CopyRequest(HttpRequestMessage oldRequest) {
var newrequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);
foreach (var header in oldRequest.Headers) {
newrequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
foreach (var property in oldRequest.Properties) {
newrequest.Properties.Add(property);
}
if (oldRequest.Content != null) newrequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
return newrequest;
}
}
答案 1 :(得分:0)
我使用@DarrelMiller的解决方案,它可以正常工作。但是,我做了一些改进
我重构了代码,因此所有内容都在CopyRequest
中,现在它以response
作为参数。
var newRequest = CopyRequest(response);
base.SendAsync(newRequest, cancellationToken)
.ContinueWith(t2 => tcs.SetResult(t2.Result));
这是经过改进的CopyRequest方法
StreamContent
并将其设置为Redirect / Found / SeeOther
的null,而是仅在需要时设置内容。private static HttpRequestMessage CopyRequest(HttpResponseMessage response)
{
var oldRequest = response.RequestMessage;
var newRequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);
if (response.Headers.Location != null)
{
if (response.Headers.Location.IsAbsoluteUri)
{
newRequest.RequestUri = response.Headers.Location;
}
else
{
newRequest.RequestUri = new Uri(newRequest.RequestUri, response.Headers.Location);
}
}
foreach (var header in oldRequest.Headers)
{
if (header.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !(oldRequest.RequestUri.Host.Equals(newRequest.RequestUri.Host)))
{
//do not leak Authorization Header to other hosts
continue;
}
newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
foreach (var property in oldRequest.Properties)
{
newRequest.Properties.Add(property);
}
if (response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther)
{
newRequest.Content = null;
newRequest.Method = HttpMethod.Get;
}
else if (oldRequest.Content != null)
{
newRequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
}
return newRequest;
}