Following the example code I'm attempting to hook up an ImageSharp.Web's AzureBlobStorageImageProvider but all image requests result in 404's. Any idea what I'm missing from my wiring-up?
Looking at the docs it seems the url convention should be https://localhost:44336/container-name/file-name.jpg?width=30
In my Startup.cs I have the following code:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddImageSharp()
.Configure<AzureBlobStorageImageProviderOptions>(options =>
{
// The "BlobContainers" collection allows registration of multiple containers.
options.BlobContainers.Add(new AzureBlobContainerClientOptions
{
ConnectionString = "REDACTED",
ContainerName = "test-container"
});
})
.AddProvider<AzureBlobStorageImageProvider>()
.Configure<AzureBlobStorageCacheOptions>(options =>
{
options.ConnectionString = "REDACTED";
options.ContainerName = "cache";
// Optionally create the cache container on startup if not already created.
AzureBlobStorageCache.CreateIfNotExists(options, PublicAccessType.None);
})
.SetCache<AzureBlobStorageCache>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseImageSharp();
}
Any ideas what I might be missing?
It seems you need to ClearProviders() first before adding the AzureBlobStorageImageProvider. So the code becomes:
.ClearProviders()
.AddProvider<AzureBlobStorageImageProvider>()
Related
I am having CORS issue on app made in DOT NET FRAMEWORK 4.5.2 & Angular 6 on frontend
This is what I tried yet.
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
//EnableCorsAttribute cors = new EnableCorsAttribute("http://localhost:4200", "*", "GET,POST");
config.EnableCors();
// Web API routes
}
I tried with adding my localhost address as well. It didnot work.
The I added a CorsPolicy as well that didnt work too.
public class MyApiCorsPolicy : Attribute, ICorsPolicyProvider
{
private System.Web.Cors.CorsPolicy _policy;
public MyApiCorsPolicy()
{
// Create a CORS policy.
_policy = new System.Web.Cors.CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true,
SupportsCredentials = true
};
}
public Task<System.Web.Cors.CorsPolicy> GetCorsPolicyAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Add the Request origin to the response.
_policy.Origins.Add(request.GetCorsRequestContext().Origin);
return Task.FromResult<System.Web.Cors.CorsPolicy>(_policy);
}
This above code is WebApiConfig.cs
This is StartUp.cs
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); ConfigureAuth(app);
ConfigureAuth(app);
}
Image is also attached
There are two changes you'll need to make in your startup.cs file. The first is in the "ConfigureServices" method:
public void ConfigureServices(IServiceCollection services)
{
//.....
services.AddCors(options =>
{
options.AddPolicy("MyCorsSettings", builder =>
builder.WithOrigins(MySettings.CorsAllowedDomains.ToArray())
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition")
.AllowAnyMethod());
});
//.....
}
Note that in the above code MySettings.CorsAllowedDomains is just a string array of allowed domains (i.e. 'https://www.example.com') that is loaded from the appSettings.
The second change is in "Configure" and you need to pass in the same reference name (i.e. "MyCorsSettings")
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
....
app.UseCors("MyCorsSettings");
....
}
This is slightly changed for .net core 6. In program.cs add service
//Add cross-orgin resources sharing services
//Defined to use with dev and staging environment
var AllowAllOrigins = "AllowAllOrigins";
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAllOrigins", policy =>
policy.WithOrigins("*")
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition")
.AllowAnyMethod());
});
//then add the middleware
app.UseCors(AllowAllOrigins);
Well, I have created an application to start on ASP net core 3.1 from scratch, I have created an API application and I have already created some layers to have better control on my application, However, when I created my object with its interface and registered them in the startup file on this way:
services.AddScoped<IMySpaceService, MySpaceService>();
I have gotten this error when I run the application:
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MySpaceService.Services.Interfaces.IMySpaceService Lifetime: Scoped ImplementationType:
this is my code:
public class MySpaceService: IMySpaceService
{
private IMySpaceRepository _mySpaceRepository;
public MySpaceService(IMySpaceRepository mySpaceRepository)
{
_mySpaceRepository = mySpaceRepository;
}
public IList<MySpaceDto> getSpaces()
{
List<MySpaceDto> spaces = new List<MySpaceDto>();
var data = _mySpaceRepository.getSpaces();
foreach (var item in data)
{
SpaceDto spaceDto = new SpaceDto();
spaceDto.Identification = item.Identification;
spaceDto.Name = item.Name;
spaces.Add(spaceDto);
}
return spaces;
}
}
My startup code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IMySpaceService, MySpaceService>();
services.AddScoped<IMySpaceRepository, MySpaceRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Any Ideas about it?.
Your MySpaceService has only one constructor with parameter IMySpaceRepository. You need to register your repository as well:
services.AddScoped<IMySpaceRepository, MySpaceRepository>();
services.AddScoped<IMySpaceService, MySpaceService>();
Well, definitely the problem was that I had not registered yet a dependency, however, the dependency that I hadn't registered was "Dbcontext" and I am calling it from my repository class on the constructor. Therefore, I have to say that your comments helped me to solve my problem because finally, it was a problem with the dependency that didn't register.
I had to do this on my startup file:
services.AddDbContext<ExampleContext>(
options => options.UseMySql("Server=localhost;port=3306;Database=exampleDB;User=UserRegistered;Password=*******", mySqlOptions => mySqlOptions
.ServerVersion(new ServerVersion(new Version(8, 0, 18), ServerType.MySql))));
I am converting my startup code into new ServiceStack Modular Startup approach and have hit a snag.
I have this code in old startup
public void Configure(IApplicationBuilder app)
{
app.UseHangfireDashboard(hangfirePath, new DashboardOptions
{
Authorization = new[] { new HangFireAuthorizationFilter() }
});
var appHost = new AppHost
{
AppSettings = settings
};
app.UseServiceStack(appHost);
var container = appHost.Resolve<Container>();
GlobalConfiguration.Configuration.UseActivator(new ContainerJobActivator(container));
app.UseHangfireServer();
}
It's important that app.UseHangfireDashboard is registered before app.UseServiceStack or the dashboard wont work.
I got it working all fine except for the part where it links the IoC container to hangfire:
GlobalConfiguration.Configuration.UseActivator(new ContainerJobActivator(container));
This is the working code without linking container:
[Priority(-1)]
public class ConfigureHangfirePostgreSql : IConfigureServices, IConfigureApp
{
IConfiguration Configuration { get; }
public ConfigureHangfirePostgreSql(IConfiguration configuration) => Configuration = configuration;
public void Configure(IServiceCollection services)
{
var conn = Configuration.GetValue<string>("database:connectionString");
services.AddHangfire((isp, config) =>
{
config.UsePostgreSqlStorage(conn, new PostgreSqlStorageOptions
{
InvisibilityTimeout = TimeSpan.FromDays(1)
});
config.UseConsole();
});
}
public void Configure(IApplicationBuilder app)
{
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangFireAuthorizationFilter() }
});
// commented out because I dont think it's possible to get container yet and also code doesn't work
//var container = app.Resolve<Container>();
//GlobalConfiguration.Configuration.UseActivator(new ContainerJobActivator(container));
app.UseHangfireServer();
}
}
I am setting the priority to -1 so it runs before servicestack is registered. Because of that I guess the container isn't yet created so I need to make another module to run after like this:
[Priority(2)]
public class ConfigureHangfirePostgreSqlPost : IConfigureApp
{
public void Configure(IApplicationBuilder app)
{
//how do I resolve container here?
}
}
The container isn't registered as a service (as I am asking the container for the service) so im not sure how I am able to access it.
What is the right way of getting hold of the container in a startup module?
It's hard to decipher what the actual question is, answering a clear one found on the last line:
What is the right way of getting hold of the container in a startup module?
This existing answer has a good summary of ASP.NET Core IOC, where you can access any singleton dependencies from app.ApplicationServices, e.g:
public void Configure(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
I would like to write integration tests for my Asp .net core application, but I don't want my tests to use real implemetation of some services.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IExternalService,ExternalService>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
}
public interface IExternalService
{
bool Verify(int id);
}
public class ExternalService : IExternalService
{
public bool Verify(int id)
{
//Implemetation is here.
//I want to fake this implemetation during testing.
}
}
[Fact]
public void TestCase()
{
//Stub out service
var myExtService = new Mock<IExternalService>();
//Setup response by stub
myExtService
.Setup(p => p.Verify(It.IsAny<int>()))
.Returns(false);
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureServices((services) =>
{
//Setup injection
services.AddTransient<IExternalService>((a) =>
{
return myExtService.Object;
});
});
var server = new TestServer(host);
var client = server.CreateClient();
var response = client.GetAsync("/").Result;
var responseString = response.Content.ReadAsStringAsync().Result;
Assert.Contains("Your service returned: False", responseString);
}
Current injection setup in test case does not work, because ExternalService is injected over the mock.
However the test will pass when I remove services.AddTransient<IExternalService,ExternalService>; from Startup.
Most likely the one in Startup is called later and all the setup in that class is preferred by application.
What options do I have to setup some dependecies in tests, but use everything else as they are declared in Startup?
UPDATE
Application code should be unaware of tests.
Tests should be aware of:
(weakly typed) Endpoint - if this changes then test should fail
IExternalService interface
Tests should not care if application has razor pages or uses mvc or how the application is wired between endpoint and IExternalService.
Tests should not have to setup or configure application (apart from stubbing IExternalService) in order to make it work.
I understand that WebHostBuilder still has to be created, but my point is that configuration should be bare minimum in test case and majority of configuration should still be described on application side.
The only option I know of is to setup WebHostBuilder with UseEnvironment:
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureServices(services =>
{
//Setup injection
services.AddTransient<IExternalService>(provider =>
{
return myExtService.Object;
});
})
.UseEnvironment("IntegrationTest");
And then add a condition in the ConfigureServices method in the Startup:
public void ConfigureServices(IServiceCollection services)
{
if (Configuration["Environment"] != "IntegrationTest")
{
services.AddTransient<IExternalService, ExternalService>();
}
services.AddMvc();
// ...
}
UPDATE:
I did some more poking around and another option is to not use UseStartup extension method but rather configure the WebHostBuilder directly. You can do this a number of ways but I thought that you could possibly create your own extension method to create a template in your tests:
public static class WebHostBuilderExt
{
public static WebHostBuilder ConfigureServicesTest(this WebHostBuilder #this, Action<IServiceCollection> configureServices)
{
#this.ConfigureServices(services =>
{
configureServices(services);
services.AddMvc();
})
.Configure(builder =>
{
builder.UseMvc();
});
return #this;
}
}
Now your tests can be setup like the following:
var host = new WebHostBuilder()
.ConfigureServicesTest(services =>
{
//Setup injection
services.AddTransient<IInternalService>(provider =>
{
return myExtService.Object;
});
});
var server = new TestServer(host);
This means that you will have to explicitly setup all the implementations that the container will resolve for the specific endpoint you are calling. You can choose to mock or use the the concrete implementations.
The only thing yo need to change is to use ConfigureTestServices instead of ConfigureServices. ConfigureTestServices runs after your Startup, therefor you can override real implementations with mocks/stubs. ConfigureServices was newer intended for that purpose, rather, it configures "host services", which are used during the host-building phase of the application, and copied into the application's DI container.
ConfigureTestServices is available in ASP Core version 2.1 and higher.
var host = new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestServices((services) =>
{
//Setup injection
services.AddTransient<IExternalService>((a) =>
{
return myExtService.Object;
});
});
So after hours of research I found a solution.
I could not find a way to solely use built-in dependency injection solution, so I opted to choose 3rd party DI solution - Autofac
Idea is to use WebHostBuilder (declared Main Program) and add necessary options so I can fake some services during testing.
Something that I learned:
If you use Startup as host.UseStartup<Startup> it will be created after host.ConfigureServices()
You cannot inject something to Startup like host.UseStartup<Startup>(new Dependency())
However if you have registred dependency in host.ConfigureServices(services => services.AddTransient<IDependency, MyDependency>()), then it will be resolved before Startup is created and constructor public Startup(IDependency dependency) is used to create Startup.
My application side:
public class Program
{
public static void Main(string[] args)
{
CreateWebHost(args)
.Build()
.Run();
}
public static IWebHostBuilder CreateWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices((services) =>
{
//Setup autofac.
services.AddAutofac();
//Register module dependency that Startup requires.
services.AddTransient<Module, MyAutofacModule>();
////It would a bit cleaner to use autofac to setup Startup dependency,
////but dependency did not get resolved for Startup.
//services.AddAutofac((builder) =>
//{
// builder.RegisterModule(new AutofacModule());
//});
})
.UseStartup<Startup>();
}
public class MyAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//Register all application dependencies in this module.
builder.Register((c) => new ExternalService()).As<IExternalService>();
}
}
public class Startup
{
private Module applicationDIModule;
public Startup(Module applicationDIModule)
{
this.applicationDIModule = applicationDIModule;
}
public void ConfigureServices(IServiceCollection services)
{
//We can add build-in services such as mvc and authorization,
//but I would not use Add(Transient/Scoped/Singleton) here.
//You should register domain specific dependecies in MyAutofacModule,
//since it will be added after this method call.
services.AddMvc();
}
//This method is called after ConfigureServices (refer to Autofac link).
public void ConfigureContainer(ContainerBuilder builder)
{
//We will register injected module.
builder.RegisterModule(applicationDIModule);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvcWithDefaultRoute();
}
}
Test case:
public class IntegrationTests
{
[Fact]
public void TestCase()
{
//Create and setup moq object as usual.
var service = new Mock<IExternalService>();
service
.Setup(p => p.Verify(It.IsAny<int>()))
.Returns(false);
//Bundle moq objects together for registration.
var attachFakes = new Action<ContainerBuilder>((builder) =>
{
builder.Register(c => service.Object);
});
//Use host builder that application uses.
var host = Program.CreateWebHost(new string[] { })
.UseContentRoot(GetContentRoot()) //Adjust content root since testproject.csproj is not in same folder as application.csproj
.ConfigureServices((services) =>
{
//We re-configure Module registration,
//so Startup is injected with our TestModule.
services.AddTransient<Module>((a) =>
{
return new TestModule(attachFakes);
});
});
//Create server to use our host and continue to test.
var server = new TestServer(host);
var client = server.CreateClient();
var response = client.GetAsync("/").Result;
var responseString = response.Content.ReadAsStringAsync().Result;
Assert.Contains("External service result: False", responseString);
}
private static string GetContentRoot()
{
var current = Directory.GetCurrentDirectory();
var parent = Directory.GetParent(current).Parent.Parent.Parent;
return Path.Combine(parent.FullName, "src");
}
}
public class TestModule : MyAutofacModule
{
private Action<ContainerBuilder> attachFakes;
public TestModule(Action<ContainerBuilder> attachFakes)
{
this.attachFakes = attachFakes;
}
protected override void Load(ContainerBuilder builder)
{
//We register everything in MyAutoFacModule before adding our fakes.
base.Load(builder);
//We add fakes and everything that is re-registered here will be used instead.
attachFakes.Invoke(builder);
}
}
Although it feels a bit brittle, but I still prefer this solution over what #ODawg suggested. His solution would work, but I see it would cause troubles in future when new test cases are added.
I work on an Web Application made with Asp Net Core and I try to use TestServer for integration testing.
I followed this blog post to setup my
test enviroment.
The Startup.cs of the application look like this :
public class Startup
{
public Startup(IHostingEnvironment env)
{
applicationPath = env.WebRootPath;
contentRootPath = env.ContentRootPath;
// Setup configuration sources.
var builder = new ConfigurationBuilder()
.SetBasePath(contentRootPath)
.AddJsonFile("appsettings.json")
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
// Many services are called here
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
// Many config are made here
loggerFactory.AddSerilog();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=auth}/{action=login}/{id?}");
});
}
}
For the integration test I use this code for create WebHostBuilder
var builder = new WebHostBuilder()
.UseContentRoot(appRootPath)
.UseStartup<TStartup>()
.UseEnvironment("test")
.ConfigureServices(x =>
{
.AddWebEncoders();
});
If I run a simple test that will check if home page is accessible it works.
For some raison I have to change some configuration in the Startup. So I add a call at Configure on WebHostBuilder :
var builder = new WebHostBuilder()
.UseContentRoot(appRootPath)
.UseStartup<TStartup>()
.UseEnvironment("test")
.ConfigureServices(x =>
{
.AddWebEncoders();
})
.Configure(x => {
// Some specific configuration
});
And, I don't know why (that's why I need your help), when I debug the same simple test like before,
the ConfigureServices and Configure method of the startup class are never called...
Even when I just let the Configure method blank.
Is this behavoir normal?
How can I set up specific configuration without adding it directly in the Startup.cs ?
WebHostBuilder.Configure replaces UseStartup, they can't be used together. Instead you can register an IStartupFilter inside ConfigureServices. Look here.