Vad*_*dim 20 .net c# rest dotnet-httpclient
下面是进行身份验证的代码,生成Authorization标头并调用API.
不幸的是,我在API上发出请求后收到401 Unauthorized错误GET.
但是,当我捕获Fiddler中的流量并重放它时,对API的调用成功,我可以看到所需的200 OK状态代码.
[Test]
public void RedirectTest()
{
HttpResponseMessage response;
var client = new HttpClient();
using (var authString = new StringContent(@"{username: ""theUser"", password: ""password""}", Encoding.UTF8, "application/json"))
{
response = client.PostAsync("http://host/api/authenticate", authString).Result;
}
string result = response.Content.ReadAsStringAsync().Result;
var authorization = JsonConvert.DeserializeObject<CustomAutorization>(result);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorization.Scheme, authorization.Token);
client.DefaultRequestHeaders.Add("Accept", "application/vnd.host+json;version=1");
response =
client.GetAsync("http://host/api/getSomething").Result;
Assert.True(response.StatusCode == HttpStatusCode.OK);
}
Run Code Online (Sandbox Code Playgroud)
当我运行此代码时,Authorization标头丢失.
但是,在Fiddler中,标题成功传递.
知道我做错了什么吗?
Chr*_*ill 51
您遇到此行为的原因是它是设计的.
遵循重定向时,大多数HTTP客户端(默认情况下)会删除授权标头.
一个原因是安全.客户端可能会被重定向到不受信任的第三方服务器,您不希望向其披露您的授权令牌.
您可以做的是检测重定向是否已发生,并将请求直接重新发送到正确的位置.
您的API将返回401 Unauthorized以指示授权标头丢失(或不完整).我将假设403 Forbidden如果请求中存在授权信息,则返回相同的API 但是不正确(用户名/密码错误).
如果是这种情况,您可以检测"重定向/丢失授权标头"组合并重新发送请求.
以下是重写的问题代码:
[Test]
public void RedirectTest()
{
// These lines are not relevant to the problem, but are included for completeness.
HttpResponseMessage response;
var client = new HttpClient();
using (var authString = new StringContent(@"{username: ""theUser"", password: ""password""}", Encoding.UTF8, "application/json"))
{
response = client.PostAsync("http://host/api/authenticate", authString).Result;
}
string result = response.Content.ReadAsStringAsync().Result;
var authorization = JsonConvert.DeserializeObject<CustomAutorization>(result);
// Relevant from this point on.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorization.Scheme, authorization.Token);
client.DefaultRequestHeaders.Add("Accept", "application/vnd.host+json;version=1");
var requestUri = new Uri("http://host/api/getSomething");
response = client.GetAsync(requestUri).Result;
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Authorization header has been set, but the server reports that it is missing.
// It was probably stripped out due to a redirect.
var finalRequestUri = response.RequestMessage.RequestUri; // contains the final location after following the redirect.
if (finalRequestUri != requestUri) // detect that a redirect actually did occur.
{
if (IsHostTrusted(finalRequestUri)) // check that we can trust the host we were redirected to.
{
response = client.GetAsync(finalRequestUri).Result; // Reissue the request. The DefaultRequestHeaders configured on the client will be used, so we don't have to set them again.
}
}
}
Assert.True(response.StatusCode == HttpStatusCode.OK);
}
private bool IsHostTrusted(Uri uri)
{
// Do whatever checks you need to do here
// to make sure that the host
// is trusted and you are happy to send it
// your authorization token.
if (uri.Host == "host")
{
return true;
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
请注意,您可以保存其值finalRequestUri并将其用于将来的请求,以避免重试中涉及的额外请求.但是,由于这是临时重定向,因此您应该每次都将请求发送到原始位置.
我会关闭自动重定向行为并创建一个客户端处理程序来隐藏处理临时重定向的代码。该HttpClient级允许你安装DelegatingHandler期从您可以修改响应的请求。
public class TemporaryRedirectHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.TemporaryRedirect)
{
var location = response.Headers.Location;
if (location == null)
{
return response;
}
using (var clone = await CloneRequest(request, location))
{
response = await base.SendAsync(clone, cancellationToken);
}
}
return response;
}
private async Task<HttpRequestMessage> CloneRequest(HttpRequestMessage request, Uri location)
{
var clone = new HttpRequestMessage(request.Method, location);
if (request.Content != null)
{
clone.Content = await CloneContent(request);
if (request.Content.Headers != null)
{
CloneHeaders(clone, request);
}
}
clone.Version = request.Version;
CloneProperties(clone, request);
CloneKeyValuePairs(clone, request);
return clone;
}
private async Task<StreamContent> CloneContent(HttpRequestMessage request)
{
var memstrm = new MemoryStream();
await request.Content.CopyToAsync(memstrm).ConfigureAwait(false);
memstrm.Position = 0;
return new StreamContent(memstrm);
}
private void CloneHeaders(HttpRequestMessage clone, HttpRequestMessage request)
{
foreach (var header in request.Content.Headers)
{
clone.Content.Headers.Add(header.Key, header.Value);
}
}
private void CloneProperties(HttpRequestMessage clone, HttpRequestMessage request)
{
foreach (KeyValuePair<string, object> prop in request.Properties)
{
clone.Properties.Add(prop);
}
}
private void CloneKeyValuePairs(HttpRequestMessage clone, HttpRequestMessage request)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
{
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以像这样实例化 HttpClient:
var handler = new TemporaryRedirectHandler()
{
InnerHandler = new HttpClientHandler()
{
AllowAutoRedirect = false
}
};
HttpClient client = new HttpClient(handler);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20095 次 |
| 最近记录: |