我正在制作一个DLL,以使用aspnetcore中的REST API。
理想情况下,我希望这样访问它:
API api = new API(clientInfo);
api.Module.Entity.Action(params);
但我正在努力实现这一目标。我无法将任何内容设为静态,因为可能会同时实例多个会话。我无法传递会话,除非通过引用,否则会话状态(cookies等)可能会在副本中更改。我应该使用一种设计模式吗?
public class API
{
private Session _session;
public API(ClientInfo clientInfo)
{
_session = new Session(clientInfo);
}
}
该会话用作客户端的中间件,在客户端需要重复登录时存储登录数据,处理一些错误/重试并公开客户端方法。
public class Session
{
private Client _client;
private string _path;
public Session(ClientInfo clientInfo)
{
_client= new Client(clientInfo);
_path = clientInfo.Path;
}
public HttpResponseMessage Get(string name, string arguments = "")
{
return _client.Get(_path, name, arguments);
}
...
}
客户端实际上执行呼叫。
public class Client
{
public HttpResponseMessage Get(string path, string endpointName, string arguments)
{
return GetClient().GetAsync(path + endpointName + arguments).Result;
}
private HttpClient GetClient(){...}
...
}
答案 0 :(得分:1)
您最大的问题之一就是HttpClient的重用。这是“预核心”时期的一个已知问题。幸运的是,它已得到解决,从Net Core 2.1开始,我们现在有了HttpClientFactory,它允许您根据需要启动并管理HttpClient,它们已作为框架的一部分进行处理。
https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore
考虑到这一点,没有什么可以阻止您使用DI注入IHttpClientFactory,这将为您提供对所需管道的必要访问。除此之外,如何设计“管理”对REST资源的访问的代码完全取决于您。也许某种存储库模式? (真的不知道您的架构就可以猜测工作)
答案 1 :(得分:0)
就我个人而言,我只是为我的API构建一个简单的客户端,并使用与该API具有的端点相对应的方法:
public class FooClient
{
private readonly HttpClient _httpClient;
public FooClient(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<GetFooResult> Get(int id)
{
...
}
// etc
}
HttpClient
依赖性是通过在Startup.cs中注册类型化的客户端来提供的:
services.AddHttpClient<FooClient>(c =>
{
// configure client
});
然后我添加一个IServiceCollection
扩展名以封装此扩展名和其他任何设置逻辑:
public static class IServiceCollectionExtensions
{
public static IServiceCollection AddFooClient(this IServiceCollection services, string uri)
{
...
}
}
然后,在启动程序中,我可以简单地执行以下操作:
services.AddFooClient(Configuration.GetValue<string>("FooUri"));
这对于通过Polly设置自动错误处理,重试策略等非常有用,因为您可以在扩展中一次设置所有配置。
现在,要解决诸如身份验证令牌之类的持久性问题,您有几种可能。我倾向于将身份验证令牌保留为声明,在这种情况下,您可以简单地检索声明并将其传递给需要它的客户端上的方法:
var foo = await _fooClient.Get(fooId, User.FindFirstValue("FooAuthToken"));
如果您以这种方式处理事情,则可以在任何范围(包括单例)中绑定客户端。
另一种方法是将auth令牌实际保留在客户端中,但这必须小心进行。除非您使用ConcurrentDictionary
之类的东西,否则绝对应该避免使用单例作用域,即使那样,要确保始终使用正确的令牌也可能有点麻烦。
假设您使用的是请求范围,则可以将令牌直接存储为ivar或其他内容,但是您仍然需要将其保存在其他地方,或者仍然需要重新进行身份验证每个请求。例如,如果要将其存储在会话中,则可以执行以下操作:
services.AddScoped<FooClient>(p =>
{
var httpClientFactory = p.GetRequiredService<IHttpClientFactory>();
var httpContextAccessor = p.GetRequiredService<IHttpContextAccessor>();
var httpClient = httpClientFactory.Create("ClientName");
var session = httpContextAccessor.HttpContext.Session;
var client = new FooClient(httpClient);
client.SetAuthToken(session["FooAuthToken"]);
});
但是,即使那样,我仍然要说将auth令牌传递到方法中比执行任何其他方法要好。关于哪些操作需要身份验证而哪些请求不需要身份验证,则更加明确,并且您始终确切知道从何而来。