使 Refit 自动在查询字符串中提供一个参数
Make Refit supply a parameter in the query string automatically
我正在使用 Refit 为 Web 服务生成客户端。
我的网站 API 的所有 URL 都是这样的:
https://service.com/api/v3/datasets?api_key=XXXXXXX
如您所见,在查询字符串中指定了 API 键而不是 header。
我希望 Refit 自动提供我的访问令牌作为查询字符串的一部分,而无需在我的服务界面中指定它。
我已经查看了文档,但还没有找到方法。
最后,我创建了一个 HttpMessageHandler
来拦截请求并使用以下代码在查询字符串中注入 "api_key":
服务创建
service = RestService.For<IMyService>(new HttpClient(new QuerystringInjectingHttpMessageHandler(("api_key", token)))
{
BaseAddress = uri,
});
处理程序
public class QuerystringInjectingHttpMessageHandler : DelegatingHandler
{
private readonly (string Key, string Value)[] injectedParameters;
public QuerystringInjectingHttpMessageHandler(params (string key, string value)[] injectedParameters) : this()
{
this.injectedParameters = injectedParameters;
}
public QuerystringInjectingHttpMessageHandler() : base(new HttpClientHandler())
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var finalUri = InjectIntoQuerystring(request.RequestUri, injectedParameters);
request.RequestUri = finalUri;
return base.SendAsync(request, cancellationToken);
}
private static Uri InjectIntoQuerystring(Uri uri, IEnumerable<(string Key, string Value)> parameters)
{
var uriStr = uri.ToString();
var queryString = new string(uriStr.SkipWhile(c => c != '?').ToArray());
var baseUri = uriStr.Substring(0, uriStr.Length - queryString.Length);
var currentParameters = HttpUtility.ParseQueryString(queryString);
foreach (var (key, value) in parameters)
{
currentParameters[key] = value;
}
var tuples = currentParameters.ToTuples();
var newUri =
string.Join("&", tuples.Select(tuple =>
{
if (tuple.name == null)
{
return tuple.value;
}
return tuple.name + "=" + tuple.value;
}));
var suffix = newUri == "" ? "" : "?" + newUri;
var finalUri = new Uri(baseUri + suffix);
return finalUri;
}
}
扩展
public static class Extensions
{
public static IEnumerable<(string name, string value)> ToTuples(this NameValueCollection collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
return
from key in collection.Cast<string>()
from value in collection.GetValues(key)
select (key, value);
}
}
我正在使用 Refit 为 Web 服务生成客户端。
我的网站 API 的所有 URL 都是这样的:
https://service.com/api/v3/datasets?api_key=XXXXXXX
如您所见,在查询字符串中指定了 API 键而不是 header。
我希望 Refit 自动提供我的访问令牌作为查询字符串的一部分,而无需在我的服务界面中指定它。
我已经查看了文档,但还没有找到方法。
最后,我创建了一个 HttpMessageHandler
来拦截请求并使用以下代码在查询字符串中注入 "api_key":
服务创建
service = RestService.For<IMyService>(new HttpClient(new QuerystringInjectingHttpMessageHandler(("api_key", token)))
{
BaseAddress = uri,
});
处理程序
public class QuerystringInjectingHttpMessageHandler : DelegatingHandler
{
private readonly (string Key, string Value)[] injectedParameters;
public QuerystringInjectingHttpMessageHandler(params (string key, string value)[] injectedParameters) : this()
{
this.injectedParameters = injectedParameters;
}
public QuerystringInjectingHttpMessageHandler() : base(new HttpClientHandler())
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var finalUri = InjectIntoQuerystring(request.RequestUri, injectedParameters);
request.RequestUri = finalUri;
return base.SendAsync(request, cancellationToken);
}
private static Uri InjectIntoQuerystring(Uri uri, IEnumerable<(string Key, string Value)> parameters)
{
var uriStr = uri.ToString();
var queryString = new string(uriStr.SkipWhile(c => c != '?').ToArray());
var baseUri = uriStr.Substring(0, uriStr.Length - queryString.Length);
var currentParameters = HttpUtility.ParseQueryString(queryString);
foreach (var (key, value) in parameters)
{
currentParameters[key] = value;
}
var tuples = currentParameters.ToTuples();
var newUri =
string.Join("&", tuples.Select(tuple =>
{
if (tuple.name == null)
{
return tuple.value;
}
return tuple.name + "=" + tuple.value;
}));
var suffix = newUri == "" ? "" : "?" + newUri;
var finalUri = new Uri(baseUri + suffix);
return finalUri;
}
}
扩展
public static class Extensions
{
public static IEnumerable<(string name, string value)> ToTuples(this NameValueCollection collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
return
from key in collection.Cast<string>()
from value in collection.GetValues(key)
select (key, value);
}
}