I there,
I've a Blazor app here that I would like to add Middleware class to it. but I can't find the startup.cs/IApplicationBuilder to add it.
My project only have a Program.cs class with a void Main method.
So how to configure a Middleware? Just adding a class named Startup.cs did not do the trick.
VS 2022/ .Net 6.0
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Mvc.Infrastructure;
// [other using]
public class Program
{
public static void Main(string[] args)
{
var builder = WebAssemblyHostBuilder
.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services
.AddTransient<DevicePresentationService>()
// [other Service registration]
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("Auth0", options.ProviderOptions);
options.ProviderOptions.ResponseType = "code";
});
var webAssemblyHost = builder.Build();
webAssemblyHost.RunAsync();
}
}
So how to configure a Middleware?
Middleware runs on the server. You posted the startup code for the Client.
Middleware is 'not applicable' in a Browser based app.
This is the new structure in .Net 6. You need to add all previous Startup.cs logic to Program.cs
To add your middleware, add:
var app = builder.Build();
and
app.MyMiddleware();
Make sure the entire namespace of the Middleware is added to your usings at the top of your file, or in global usings.
Documentation
EDIT: I didn't see that this a web assembly Blazor application. #Henk Holterman is correct, you cannot use middleware on a client only application. You will need to add the middleware to your Server.
Related
I have an asp.net core web api that I created from the wizard - and it's generated code like this:
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
...
And if I run it, I get a swagger url, and I can go to
https://localhost:7100/swagger/v1/swagger.yaml
and get a yaml file... that's all good.
The Question
However, I need that yaml file in the CI/CD process for various reasons, so I want to get it dynamically after building, but starting a web server, calling it, and bringing it down has a lot of ways it can go wrong. What I'd like to do is just make a command line option to get the swagger file that never starts something listening on http. so I can just run "dotnet run myApi generate_swagger_yaml >swagger.yaml" Something like:
public static void Main(string[] args)
{
if (args[0] == "generate_swagger_yaml")
{
var yamlFile = ....somehow get the swagger yaml file...
Console.Writeline( yamlfile );
return 0;
}
...
but I can't figure out how to do it.
I would actually recommend not building this into your application directly. Swashbuckle (the library which actually generates the swagger doc at runtime) offers a CLI tool which can take an assembly DLL and spit out the swagger all in one command.
Check it out here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcorecli
This can be used by your CI/CD to output the Swagger/OpenAPI document to a file .
Since my last answer, I've had to experiment more with Swashbuckle and Swagger generation and I've found the actual answer to your original question.
Once you create your builder:
var app = builder.Build();
You need to fetch the IServiceProvider from it and use it to resolve an instance of ISwaggerProvider. With this, you can get the swagger document and render it to Json or Yaml.
var swaggerProvider = app.Services.GetRequiredService<ISwaggerProvider>();
var swagger = swaggerProvider.GetSwagger("v1");
var stringWriter = new StringWriter();
swagger.SerializeAsV3(new OpenApiYamlWriter(stringWriter));
var swaggerYaml = stringWriter.ToString();
Console.WriteLine(swaggerYaml);
Where "v1" should be the default name of your swagger doc which you can specify when setting it up like so:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v7", null);
});
Being able to customize the Host configuration in integration tests is useful to avoid calling services that shouldn't run at all during testing. This was easily achievable using the standard Startup model, overriding the CreateHostBuilder from the WebApplicationFactory. I tried many many things using the "Minimal APIs" approach and couldn't figure it out.
A full example of what was possible using the Startup model, would be something like this:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
// This is a service that downloads Configuration from the internet, to be used
// as a source for `IConfiguration`, just like any `appsettings.json`.
// I don't want this running during testing
.AddConfigServer();
}
As you can imagine, the AddConfigServer calls an external web server to download the configuration I want to use in my app startup, but I definitely don't want my integration tests calling this external web server for several reasons: don't want to depend on external services, don't want to hammer my config server with testing requests, don't want my server customizations to be exposed to my tests, etc.
Using the Startup model, I could easily change this behavior with this:
public class MyWebApplicationFactory : WebApplicationFactory<Program>
{
protected override IHostBuilder CreateHostBuilder() =>
Host.CreateDefaultBuilder()
// No AddConfigServer for my integration tests
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
//... other customizations
}
However, trying this same approach throws an exception on my integration test startup:
System.InvalidOperationException : No application configured. Please specify an application via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, or specifying the startup assembly via StartupAssemblyKey in the web host configuration.
Since I don't have a Startup class, I couldn't figure out how to properly do this. In fact, since this is common in all my web projects, I abandoned Minimal APIs altogether for the moment, until I figure out how to properly achieve this without a Startup class.
PS.: I have all the "partial Program" and "InternalsVisibleTo" in place, as described here.
In order to make this work, you have to provide your configuration values twice both before and after the minimal API Program.cs runs.
Here's how I did it:
public class MyFixture : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
var configurationValues = new Dictionary<string, string>
{
{ "MyConfigSetting", "Value" }
};
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(configurationValues)
.Build();
builder
// This configuration is used during the creation of the application
// (e.g. BEFORE WebApplication.CreateBuilder(args) is called in Program.cs).
.UseConfiguration(configuration)
.ConfigureAppConfiguration(configurationBuilder =>
{
// This overrides configuration settings that were added as part
// of building the Host (e.g. calling WebApplication.CreateBuilder(args)).
configurationBuilder.AddInMemoryCollection(configurationValues);
});
}
}
I'm trying to write integration tests for web api, I have two appsettings.json files one for api project and other for integration tests. I want to use the values(azure storage connection strings) from integration testing project in Startup class of web api.
I have tried creating CustomWebApplicatonFactory it didn't work since the Startup class of web api gets settings like shown below.
//Configure services method in Startup class
public virtual void ConfigureServices(IServiceCollection services)
{
var settings = ConfigurationProvider.GetConfiguration();
services.TryAddSingleton(settings);
services.AddHttpClient();
var azureTableStorageConnectionString =
settings["AzureMainStoreConnectionStringSecretName"];
//Other Startup related code
}
I want to change the value of "azureTableStorageConnectionString" from my integration test project. Help and suggestions are much appreciated.
First of all, instead of using the static function ConfigurationProvider.GetConfiguration() inject IConfiguration into your Startup class. The host that is usually defined in the Program class builds that for you so you can inject it.
Then instead of using WebApplicationFactory<TStartup> you can build a test-host yourself like this.
[Fact]
public async Task LoadWeatherForecastAsync()
{
var webHostBuilder = new WebHostBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureAppConfiguration(builder =>
{
builder.Sources.Clear();
builder.SetBasePath(AppContext.BaseDirectory);
// this is an appsettings file located in the test-project!
builder.AddJsonFile("appsettings.Testing.json", false);
})
.UseStartup<Startup>();
var host = new TestServer(webHostBuilder);
var response = await host.CreateClient().GetAsync("weatherforecast");
Assert.True(response.IsSuccessStatusCode);
}
I've created a sample on github. You can clone it and try it out.
I am building a support library for ASP.NET Core websites. I have a few pieces of middleware that need to be enabled, and they need to be added before any other middleware due what they do.
I can create an extension method on IWebHostBuilder to add services, likewise for configuring logging, but I don't see any way to add middleware in a programmatic way. Is there any way to do this? Looking at the source for WebHost/WebHostBuilder nothing jumped out.
Given the first comment, I may not have been clear enough. I know how to create middleware and use it. What I am trying to do is ensure that when the Configure(IApplicationBuilder app) method is called on Startup by the framework, my middleware is already in place. In a similar manner to being able to do ServiceConfiguration prior to Startup even being created. So an extension method like
public static IWebHostBuilder AddPayscaleHostingServices(this IWebHostBuilder webHostBuilder, string serviceName)
{
return webHostBuilder.ConfigureServices(collection =>
{
collection.RegisterPayscaleHostingServices();
}).ConfigureLogging(factory =>
{
});
}
gives me the ability to do some setup prior to the webHostBuilder.Build method, but I don't see anything similar for middleware/anything on IApplicationBuilder.
Thanks,
Erick
You could use a startup filter to achieve this. Startup filters allow you to configure middleware from a service resolved from the DI container.
Defining a startup filter is easy:
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
// Configure middleware
// ...
// Call the next configure method
next(app);
};
}
}
Always make sure to call next(app) or any other middleware won't be configured.
Now register the startup filter as a singleton implementation of IStartupFilter in your ConfigureServices method:
services.AddSingleton<IStartupFilter, MyStartupFilter>();
I'm very new to ASP.NET and am attempting to pass an object between two controllers in a web application I'm making in Visual Studio 2015. The web application is using an ASP.Net 5 Preview Template Web application (if it helps, I think I'm using beta code 7 and I'm not building for DNX Core 5).
The problem I'm having is whenever I try to put anything into the TempData variable, the program seems to crash. For example, in a "Create" method I have:
[HttpPost]
public ActionResult Create(Query query)
{
switch (query.QueryTypeID)
{
case 1:
TempData["Test"] = "Test";
return RedirectToAction("Index", "EventResults");
case 2:
break;
default:
break;
}
return View();
}
In that method, I attempt to add a simple test string under the key "test". When I run the application with that TempData statement in there, I receive an error message stating
An unhandled exception occurred while processing the request.
InvalidOperationException: Session has not been configured for this application >or request.
Microsoft.AspNet.Http.Internal.DefaultHttpContext.get_Session()
I have tried going to the Web.config located in the wwwroot element of the project and adding a "sessionState" object into a "system.web" element, but this had no effect on the error.
Any help would be very much so appreciated as I've been looking for solutions for this everywhere. I'm hoping it's something stupid/blindingly obvious that I somehow missed.
In order to use middleware, such as Session, Cache, etc in ASP.NET 5, you have to enable them explicitly.
Enabling session is done by adding the appropriate nuget package in your project.json file's dependencies section (make sure that the package version matches the versions of the other dependencies you have added):
"Microsoft.AspNet.Session": "1.0.0-*"
and the appropriate session (cache) storage package as well (like the example below; in memory):
"Microsoft.Extensions.Caching.Memory": "1.0.0-*"
and adding the middleware to dependency resolution in the Startup.cs Service configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddCaching();
services.AddSession(/* options go here */);
}
and adding the middleware to OWIN in the Startup.cs OWIN configuration:
public void Configure(IApplicationBuilder app)
{
app.UseSession();
//...
Make sure that the UseSession comes before the MVC configuration.
For Asp.Net Core, make sure Asp.NetCore.Session is added.
You can configure session in StartUp.cs like below.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.CookieHttpOnly = true;
});
}
public void Configure(IApplicationBuilder app)
{
app.UseSession();
app.UseMvcWithDefaultRoute();
}