我在WebAPI项目中使用Autofac和OWIN,该项目是从头开始构建的(与VS2015中提供的完整WebAPI模板相对应)。不可否认,我是这样做的新手。
在单元测试项目中,我在单元测试开始时设置了一个OWIN Startup类:
WebApp.Start<Startup>("http://localhost:9000/")
Startup类如下:
[assembly: OwinStartup(typeof(API.Specs.Startup))]
namespace API.Specs
{
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
//config.Filters.Add(new AccessControlAttribute());
config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
config.Formatters.JsonFormatter.SerializerSettings = Serializer.Settings;
config.MapHttpAttributeRoutes();
// Autofac configuration
var builder = new ContainerBuilder();
// Unit of Work
var unitOfWork = new Mock<IUnitOfWork>();
builder.RegisterInstance(unitOfWork.Object).As<IUnitOfWork>();
// Principal
var principal = new Mock<IPrincipal>();
principal.Setup(p => p.IsInRole("admin")).Returns(true);
principal.SetupGet(p => p.Identity.Name).Returns('test.user');
principal.SetupGet(p => p.Identity.IsAuthenticated).Returns(true);
Thread.CurrentPrincipal = principal.Object;
if (HttpContext.Current != null)
{
HttpContext.Current.User = new GenericPrincipal(principal.Object.Identity, null);
}
builder.Register(c => principal).As<IPrincipal>();
.
.
.
// Set up dependencies for Controllers, Services & Repositories
.
.
.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
appBuilder.UseWebApi(config);
}
private static void RegisterAssemblies<TModel, TController, TService, TRepoClass, TRepoInterface>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork)
where TModel : class
where TRepoClass : class
where TService : class
{
RegisterController<TController>(ref builder);
var repositoryInstance = RegisterRepository<TRepoClass, TRepoInterface>(ref builder);
RegisterService<TService>(ref builder, ref unitOfWork, repositoryInstance);
}
private static void RegisterController<TController>(ref ContainerBuilder builder)
{
builder.RegisterApiControllers(typeof(TController).Assembly);
}
private static object RegisterRepository<TRepoClass, TRepoInterface>(ref ContainerBuilder builder)
where TRepoClass : class
{
var constructorArguments = new object[] { DataContexts.Instantiate };
var repositoryInstance = Activator.CreateInstance(typeof(TRepoClass), constructorArguments);
builder.RegisterInstance(repositoryInstance).As<TRepoInterface>();
return repositoryInstance;
}
private static void RegisterService<TService>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork, object repositoryInstance)
where TService : class
{
var constructorArguments = new[] { repositoryInstance, unitOfWork.Object};
var serviceInstance = Activator.CreateInstance(typeof(TService), constructorArguments);
builder.RegisterAssemblyTypes(typeof(TService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
builder.RegisterInstance(serviceInstance);
}
}
}
附注:理想情况下,我想将Principle设置为测试的一部分,以便能够将不同的用户传递给控制器,但是如果我必须在启动时保持CurrentPrincipal / User的设置上课,我可以解决它。
使用DI访问我的控制器时,启动类工作正常,但RequestContext.Principal
中的Principal永远不会设置。它始终为空。我打算使用Request上下文的方式如下:
[HttpGet]
[Route("path/{testId}")]
[ResponseType(typeof(Test))]
public IHttpActionResult Get(string testId)
{
return Ok(_service.GetById(testId, RequestContext.Principal.Identity.Name));
}
我还尝试将模拟的主要类注入到我的Controller的构造函数中作为一种解决方法 - 我使用与通用方法中显示的相同方法来使用DI设置我的服务。但是,我的构造函数中只有null。
此时我已经和这个问题坐了大约一天,拉了我的头发。任何帮助,将不胜感激。提前谢谢。
答案 0 :(得分:2)
我避免使用DI执行此操作。您需要在请求上下文中设置主体,而不是将主体注入构造函数。
以下是我做的事情,如果是我的话:
首先,我不会嘲笑那些不需要嘲笑的事情。也就是说,您的IIdentity
实现实际上可能是真实对象。
private static IPrincipal CreatePrincipal()
{
var identity = new GenericIdentity("test.user", "test");
var roles = new string[] { "admin" };
return new GenericPrincipal(identity);
}
接下来,您需要在每个&#34;请求&#34;上运行设置。您通过测试应用程序进行处理。我猜这是更多&#34;集成测试&#34;比单元测试&#34;因为你使用了整个启动课程和所有内容,所以你不能只设置一次校长并完成。它必须在每个请求上完成,就像真正的身份验证操作一样。
最简单的方法是使用a simple delegating handler。
public class TestAuthHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Set the principal. Whether you set the thread principal
// is optional but you should really use the request context
// principal exclusively when checking permissions.
request.GetRequestContext().Principal = CreatePrincipal();
// Let the request proceed through the rest of the pipeline.
return await base.SendAsync(request, cancellationToken);
}
}
最后,将该处理程序添加到HttpConfiguration
类中的Startup
管道。
config.MessageHandlers.Add(new TestAuthHandler());
应该这样做。请求现在应该通过该auth处理程序并获得分配的主体。