I am trying to put Startup class back to .NET 6 project Web API.
So I just altered Program.cs to:
public static class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
and I extracted all service registrations and stuff into my Startup class:
public class Startup
{
public WebApplication InitializeApp()
{
var builder = WebApplication.CreateBuilder();
IServiceCollection servicesservices = builder.Services;
ConfigureServices(servicesservices, builder);
var app = builder.Build();
Configure(app);
return app;
}
public void ConfigureServices(IServiceCollection services, WebApplicationBuilder builder)
{
ConfigurationManager configuration = builder.Configuration;
services.AddApplicationLayer();
services.AddPersistanceLayer(builder.Configuration);
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "VehicleReservation", Version = "v1" });
});
}
public void Configure(WebApplication app)
{
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseErrorHandlingMiddleware();
app.UseAuthorization();
app.MapControllers();
SeedDatabase(app);
}
private void SeedDatabase(WebApplication app)
{
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var vehicleReservationContext = services.GetRequiredService<VehicleReservationContext>();
VehicleReservationContextSeed.SeedAsync(vehicleReservationContext);
}
}
}
but I have an error: `The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection.'
I see that I have 2 params for my ConfigureServices, but Im not sure, how to make it to have 1 or non parameters?
APPROACH #1:
Is to have _builder globally defined within Startup class
public class Startup
{
private WebApplicationBuilder _builder;
public Startup(WebApplicationBuilder builder)
{
_builder = builder;
}
public WebApplication InitializeApp()
{
var _builder = WebApplication.CreateBuilder();
IServiceCollection servicesservices = _builder.Services;
ConfigureServices(servicesservices);
var app = _builder.Build();
Configure(app);
return app;
}
public void ConfigureServices(IServiceCollection services)
{
ConfigurationManager configuration = _builder.Configuration;
services.AddApplicationLayer();
services.AddPersistanceLayer(_builder.Configuration);
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "VehicleReservation", Version = "v1" });
});
}
public void Configure(WebApplication app)
{
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseErrorHandlingMiddleware();
app.UseAuthorization();
app.MapControllers();
SeedDatabase(app);
}
private void SeedDatabase(WebApplication app)
{
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var vehicleReservationContext = services.GetRequiredService<VehicleReservationContext>();
VehicleReservationContextSeed.SeedAsync(vehicleReservationContext);
}
}
}
Startup classes support configuration injection via ctor, so since you are you only using Configuration from WebApplicationBuilder just inject it:
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// ...
// to clarify configure method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// ...
}
And then you can use Configuration property in ConfigureServices.
See the docs on the Startup class.
Related
I have the Base URL within the appsettings.json like below
"RM": {
"BaseAddress": "https://rm-dev.abc.org/"
},
With in the Class where I am trying to make a call this endpoint
public class InventorySearchLogic
{
private readonly SMContext _context;
private readonly IConfiguration _iconfiguration;
public InventorySearchLogic(SMContext context, IConfiguration iconfiguration)
{
_context = context;
_iconfiguration = iconfiguration;
}
public InventorySearchLogic(SMContext context)
{
}
public async Task<string> GetRoomID(string roomName)
{
//string rmID = "";
using (var client = new HttpClient())
{
RmRoom retRoom = new RmRoom();
client.BaseAddress = new Uri(_iconfiguration.GetSection("RM").GetSection("BaseAddress").Value);
client.DefaultRequestHeaders.Accept.Clear();
When debugging it throws error like System.NullReferenceException: Message=Object reference not set to an instance of an object. how to access the base URL from appsettings.json
I am not sure how to use the ConfigurationBuilder() as I have different apSettings.json file one for each environment like appsettings.Development.json , appsettings.QA.json, appsettings.PROD.json
Below is my Startup
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.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.AddControllersWithViews();
services.AddSession();
services.AddMemoryCache();
services.AddDbContextPool<SurplusMouseContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("SMConnectionStrings"),
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure();
});
});
services.AddHttpContextAccessor();
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Customers}/{action=Index}/{id?}");
});
}
}
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>();
});
}
You're already injecting an IConfiguration in your service and saving it as _iconfiguration. Assuming that's not the null value, then simply use .GetValue to retrieve a value.
string baseAddress = _iconfiguration.GetSection("RM").GetValue<string>("BaseAddress");
Read more about ASP.Net configuration
Well, it seems that _iconfiguration is also null.
You've indicated in the comments that you're creating an instance of InventorySearchLogic from a controller, such as
// inside controller logic
var searchLogic = new InventorySearchLogic(_context, _iconfiguration);
This is the wrong approach. Instead, you should register this class as a DI service (although you should also add an interface, it's not necessary right now).
In your Startup's ConfigureServices method, add
services.AddTransient<InventorySearchLogic>();
Then instead of manually creating a variable of type InventorySearchLogic, request it though DI
// your controller constructor
private readonly InventorySearchLogic searchLogic;
public MyController(InventorySearchLogic searchLogic)
{
this.searchLogic = searchLogic;
}
This way, InventorySearchLogic's constructor correctly gets the DI services it's looking for. You will have to move the SMContext context. Maybe move that to the method's parameters?
I tried to inject my own service in my api controller, but it throws an InvalidOperationException. It is unable to resolve the service. (.NET 5)
What I am doing wrong?
public class MyController : ControllerBase
{
private readonly MyService _myService;
public ContractsController(MyService service)
{
myService = service;
}
[HttpGet("{id}")]
public Item GetItemById(string id)
{
return _myService.getContract(id);
}
}
This is my Startup file:
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.AddSwaggerGen(c =>
{
c.CustomOperationIds(selector => $"{selector.HttpMethod.ToLower().Pascalize()}{selector.ActionDescriptor.RouteValues["controller"]}");
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My.Portal.Server", Version = "v1" });
});
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.SetIsOriginAllowed(origin => new Uri(origin).IsLoopback)
.WithOrigins(Configuration.GetSection("AllowedOrigins").Get<string[]>())
.AllowAnyHeader()
.AllowAnyMethod();
});
});
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My.Portal.Server v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
If i create a new instance in the constructor, then it is working.
thank you in advance!
Example, as per request in comment:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IInfoStorageService>(c => new InfoStorageService(Configuration["ImageInfoConnectionString"]));
This is telling the application to pass an instance of InfoStorageService in where IInfoStorageService is injected. It is instantiated with a connection string that it reads from configuration.
AddScoped means that the injected service will be the same across the request. You can also use AddTransient() which means it will not match any other uses of the service, or you can use AddSingleton(), which means it will use the same instance through the whole lifetime of the application.
I have a web api application which is working fine in 2.1.
I am using same application to host in IIS on windows and without IIS on linux.
Now I am trying to upgrade the application.
I have upgraded the nuget packages and project version successfully.Now when trying to debug app looks there is some problem in my congiruation startup class which is as below
public static void Main(string[] args)
{
StartupShutdownHandler.BuildWebHost(args).Build().Run();
}
namespace MyApp
{
public class StartupShutdownHandler
{
public static IWebHostBuilder BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args)
.UseStartup<StartupShutdownHandler>();
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public StartupShutdownHandler(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.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; }).AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters(); //this is changed in 3.0
services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; }).AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
CorsRelatedPolicyAddition(services);
}
private void CorsRelatedPolicyAddition(IServiceCollection services)
{
var lstofCors = ConfigurationHandler.GetSection<List<string>>(StringConstants.AppSettingsKeys.CorsWhitelistedUrl);
if (lstofCors != null && lstofCors.Count > 0 && lstofCors.Any(h => !string.IsNullOrWhiteSpace(h)))
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins(lstofCors.ToArray()).AllowAnyMethod().AllowAnyHeader(); });
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(MyAllowSpecificOrigins);
//app.UseMvc(); //this is changed in 3.0
applicationLifetime.ApplicationStarted.Register(StartedApplication);
applicationLifetime.ApplicationStopping.Register(OnShutdown);
}
private void OnShutdown()
{
Logger.Debug("Application Shutdown");
}
private void StartedApplication()
{
Logger.Debug("Application Started");
}
}
}
I have tried chagned some lines which are commented as //this is changed in 3.0 but it doesn't work.
Please identify the problem
Following changes eventually work for 2.1 to 3.0 path.
One manual change i am doing is updating newtonsoft to new builtin json type in all places where it doesn't break
(e.g for one case i have to still use newtonsoft where i am serializing Formcollection and QueryCollection of the request)
namespace MyApp.Interfaces
{
public class StartupShutdownHandler
{
public static IWebHostBuilder BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args).
ConfigureKestrel(serverOptions =>{}).UseIISIntegration()
.UseStartup<StartupShutdownHandler>();
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public StartupShutdownHandler(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.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddControllers(options => options.RespectBrowserAcceptHeader = true).AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters(); //updated
CorsRelatedPolicyAddition(services);
}
private void CorsRelatedPolicyAddition(IServiceCollection services)
{
var lstofCors = ConfigurationHandler.GetSection<List<string>>(StringConstants.AppSettingsKeys.CorsWhitelistedUrl);
if (lstofCors != null && lstofCors.Count > 0 && lstofCors.Any(h => !string.IsNullOrWhiteSpace(h)))
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins(lstofCors.ToArray()).AllowAnyMethod().AllowAnyHeader(); });
});
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
applicationLifetime.ApplicationStarted.Register(StartedApplication);
applicationLifetime.ApplicationStopping.Register(OnShutdown);
}
private void OnShutdown()
{
Logger.Debug("Application Ended");
}
private void StartedApplication()
{
Logger.Debug("Application Started");
}
}
}
I have some trouble i try to resolve problem i use Autofac with .net core 3.0-6preview.
I add new AutofacServiceProviderFactory() to CreateHostBuilder which is require in this .net core version framework.
The code was working correctly in version 2.1 and lower but now application was crashed
The exception:
System.NotSupportedException: 'ConfigureServices returning an System.IServiceProvider isn't supported.'
The Program class code:
public class Program
{
public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
And the Startup class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IContainer ApplicationContainer { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new ContainerModule(Configuration));
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApplicationLifetime appLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
var jwtSettings = app.ApplicationServices.GetService<JwtSettings>();
var generalSettings = app.ApplicationServices.GetService<GeneralSettings>();
if (generalSettings.SeedData)
{
var dataInitializer = app.ApplicationServices.GetService<IDataInitializer>();
dataInitializer.SeedAsync();
}
// app.UseMvc();
appLifetime.ApplicationStopped.Register(() => ApplicationContainer.Dispose());
}
}
Startup syntax has changed for configuring Autofac.
Instead, in Startup do the following
public void ConfigureServices(IServiceCollection services) {
//... normal registration here
// Add services to the collection. Don't build or return
// any IServiceProvider or the ConfigureContainer method
// won't get called.
}
public void ConfigureContainer(ContainerBuilder builder) {
//configure auto fac here
builder.RegisterModule(new ContainerModule(Configuration));
}
Reference Autofac documentation for ASP.NET Core 3.0+
you must change program.c
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(options => options.ClearProviders())
.UseStartup<Startup>();
I am trying to add multiple rul which should be whitelisted for CORS.
Problem is only single url work. My code is as following
public class StartupShutdownHandler
{
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public StartupShutdownHandler(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.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; }).AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters();
CorsRelatedPolicyAddition(services);
}
private void CorsRelatedPolicyAddition(IServiceCollection services)
{
/* var lstofCors = ConfigurationHandler.GetSection<List<string>>(StringConstants.AppSettingsKeys.CORSWhitelistedURL);
*/
var lstofCors = new List<string> {"url1", "url2"}
if (lstofCors != null && lstofCors.Count > 0 && lstofCors.Any(h => !string.IsNullOrWhiteSpace(h)))
{
services.AddCors(options =>
{
//https://stackoverflow.com/questions/43985620/asp-net-core-use-multiple-cors-policies
foreach (var entry in lstofCors.FindAll(h => !string.IsNullOrWhiteSpace(h)))
{
options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins(entry); });
}
});
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(MyAllowSpecificOrigins);
// CheckAndEnableDetailLogs(app);
app.UseMvc();
}
}
You are overriding the options for every entry in your array.
Just add the entries as an array of strings.
var lstofCors = new string[] {"url1", "url2"};
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins(lstofCors.ToArray()).AllowAnyMethod(); });
});
}
Edit:
I have added AllowAnyMethod() as looks like only get method is working