Difference between instance of HttpClient and IHttpClientFactory in .NET Core5 - asp.net-core

Below is my Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
And controller:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly HttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
public TestController(HttpClient httpClient, IHttpClientFactory httpClientFactory)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
}
[HttpGet]
[Route("getdata")]
public async Task GetData()
{
var baseAddress = "http://youtube.com";
var response = await _httpClient.GetAsync(baseAddress);
var client = _httpClientFactory.CreateClient();
response = await client.GetAsync(baseAddress);
}
}
As you can see I can get an instance of HttpClient in two ways:
By Injecting HttpClient
By Injecting IHttpClientFactory and then _httpClientFactory.CreateClient();
Though I am getting responses using both instances, my question is what is the difference between them? And when to use which one?

If you just post signal request.there's even no difference between injecting an instance of httpclient and creating a instance of httpclient with httpclientfactory .
If you use several instances of httpclient or reuse httpclient to post multiple requests, some problems may occur, using httpclient could handle these problems.
You could create httpclients with different settings as follows:
In startup.cs:
services.AddHttpClient("client_1", config =>
{
config.BaseAddress = new Uri("http://client_1.com");
config.DefaultRequestHeaders.Add("header_1", "header_1");
});
services.AddHttpClient("client_2", config =>
{
config.BaseAddress = new Uri("http://client_2.com");
config.DefaultRequestHeaders.Add("header_2", "header_2");
});
In your controller:
var client1 = _httpClientFactory.CreateClient("client_1");
var client2 = _httpClientFactory.CreateClient("client_2");

Related

how to set the output type List<string> in a middleware in .NET Core 2.1?

I have this middleware class when I want to show a List<string> in the output:
namespace WebAspNetCore2_1
{
public class LearningMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LearningMiddleware> _logger_log;
private readonly List<string> logger;
public LearningMiddleware(RequestDelegate next,ILogger<LearningMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions .Logging.LogLevel.Information,"information of logger",logger[0]);
return _next(httpContext);
}
}
}
I have debugged my code but seen to be correct, my List<> is filled, I don't know why the compiler is throwing this exception:
InvalidOperationException: Could not create an instance of type Microsoft.Extensions.Logging.ILogger`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'logger' parameter a non-null default value.
i thought was the order declaration in StartUp, but not
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// app.UseLearningMiddleware();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMiddleware<LearningMiddleware>();
app.UseMvc();
}
link in video for detail evidence: https://youtu.be/2FoLvhLweYo
I tested your code in my side but it worked well... I created a new asp.net core 2.1 MVC project and create a middleware. In StartUp.cs, I put app.UseMiddleware<MyMiddleware>(); just before app.UseMvc(routes =>
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApplication2
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<MyMiddleware> _logger_log;
private readonly List<string> logger;
public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger_log)
{
_next = next;
_logger_log = logger_log;
List<string> _logger = new List<string>
{
("EUR/USD"),
("1.0500")
};
logger = _logger;
}
public Task Invoke(HttpContext httpContext)
{
_logger_log.Log(Microsoft.Extensions.Logging.LogLevel.Information, "information of logger", logger[0]);
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
}

How to inject custom service on startup in .NET Core 5

I want to read my data from database and control it, and I need to do this in the request pipeline at startup.
So I have to do dependency injection at startup.
This is my (DI)
public Startup(IConfiguration configuration,IAuthHelper authHelper)
{
Configuration = configuration;
AuthHelper = authHelper;
}
public IConfiguration Configuration { get; }
public IAuthHelper AuthHelper;
I encounter this error
An error occurred while starting the application.
InvalidOperationException: Unable to resolve service for type 'Laboratory.Core.Services.Interfaces.IAuthHelper' while attempting to activate 'Laboratory.Startup'.
I used service like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var siteDirectory = AuthHelper.GetSiteSetting().MediaPath;
var fileServerOptions = new FileServerOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine
(env.WebRootPath, $#"{siteDirectory}User Picture\")),
RequestPath = "/ServerFiles"
};
app.UseFileServer(fileServerOptions);
}
This is my service
public class AuthHelper : IAuthHelper
{
private readonly LaboratoryContext _context;
private readonly IRazorPartialToStringRenderer _renderer;
private readonly IHttpContextAccessor _httpContext;
private readonly IHttpClientFactory _clientFactory;
public AuthHelper(LaboratoryContext context, IRazorPartialToStringRenderer renderer, IHttpContextAccessor httpContext, IHttpClientFactory clientFactory)
{
_context = context;
_renderer = renderer;
_httpContext = httpContext;
_clientFactory = clientFactory;
}
public TableSiteSetting GetSiteSetting()
{
try
{
return _context.TableSiteSettings.AsNoTracking().FirstOrDefault();
}
catch (SqlException)
{
return new TableSiteSetting() { StaticIp = "ServerConnectionError" };
}
catch (Exception)
{
return new TableSiteSetting() { StaticIp = "ServerError" };
}
}
}
Thanks for any help.
Your service can't be injected in Startup constructor because it has not been added yet to the dependency injection container. But you can inject it to the Configure method.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAuthHelper authHelper)
{
...
}
I assume you have already registered the service in ConfigureServices
services.AddSingleton<IAuthHelper, AuthHelper>(); // Or scoped/transient depends what your service does.
You can get dbcontext service in program.cs and do what ever you like to your database data.
for example I use this approach to seed my database:
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
await ApplicationDbContextSeed.SeedSampleDataAsync(context)
}
host.Run();

How do I build tests using xUnit for an Asp.NetCore WebAPI built with Entity Framework Core and Simple Injector?

I have created an ASP.NET Core Web API using Entity Framework Core and Simple Injector.
I would like unit tests using xUnit to test my controllers.
I'm not sure where to begin. I believe that I have to mock a container object in my unit tests.
Here is the start up code where the container gets initialized:
public class Startup
{
private Container container;
public IConfiguration Configuration { get; }
private IConfigurationRoot configurationRoot;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
// Build configuration info
configurationRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
InitializeContainer();
services.AddSimpleInjector(container, options =>
{
options.AddAspNetCore()
.AddControllerActivation();
options.AddLogging();
});
}
private void InitializeContainer()
{
container = new SimpleInjector.Container();
container.Options.ResolveUnregisteredConcreteTypes = false;
container.ConfigureServices();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseSimpleInjector(container);
AppSettingsHelper.AppConfig = configurationRoot;
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Here is the code for my services installer:
public static class ServicesInstaller
{
public static void ConfigureServices(this Container container)
{
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
//Assembly.Load will not re-load already loaded Assemblies
container.Register<IFooContext, FooContext>(Lifestyle.Scoped);
container.Register<FooContext>(Lifestyle.Scoped);
}
}
Here is a sample controller:
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
private readonly ILogger<SomeController> _logger;
private readonly Container _container;
public SomeController(ILogger<SomeController> p_Logger, Container p_Container)
{
_logger = p_Logger;
_container = p_Container;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetOwnerByOwnerId(Guid p_SomeId)
{
Some some;
using (Scope scope = AsyncScopedLifestyle.BeginScope(_container))
{
var dbContext = _container.GetInstance<FooContext>();
some = dbContext.Somes.Where(x => x.SomeId == p_SomeId).FirstOrDefault();
}
return some;
}
}
I'm relatively new to using SimpleInjector.
How would I mock up a container to use for testing?
The controller in the provided example should not be coupled to anything container specific.
Explicitly inject the necessary dependencies into the controller.
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase {
private readonly ILogger<SomeController> _logger;
private readonly IFooContext dbContext;
public SomeController(ILogger<SomeController> p_Logger, IFooContext dbContext) {
_logger = p_Logger;
this.dbContext = dbContext;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetOwnerByOwnerId(Guid p_SomeId) {
Some some = dbContext.Somes.Where(x => x.SomeId == p_SomeId).FirstOrDefault();
return some;
}
}
Now there is no need to mock the container, which would be seen as an implementation detail code smell.
Mock the dependency abstractions and verify the expected behavior when exercising your unit test(s).
Controllers should also be kept as lean as possible since most other cross-cutting concerns, like making sure the injected context is scoped, are handled by the framework via the configured container at startup.

ASP.NET Core 2.2 - accessing the StaticFileOption RequestPath later in code

In Startup.cs Configure function I do something like this:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(#"\\server\somepath\someimages"),
RequestPath = "/images"
});
Later, say in the controller, I'd like to not hard-code:
string ImageImLookingFor = "/images" + foo.jpg;
Instead I'd like to do something like:
string ImageImLookingFor = SomeObjectThatGivesMe.RequestPath + foo.jpg;
Is this possible?
Not entirely sure if it is possible but a workaround can be an appsettings key and read it from both locations.
ex:
in your appsettings
{
"ImagesPath" : '/images"
}
in Starup.cs
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(#"\\server\somepath\someimages"),
RequestPath = Configuration["ImagesPath"]
});
In your contorller
string ImageImLookingFor = configuration.RequestPath + foo.jpg;
You can make the configuration file a strong type and replace it with IOptions<ImageConfiguration> where ImageConfiguration is a class that has ImagesPath property
You could try to configure StaticFileOptions with services.Configure like
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<StaticFileOptions>(options => {
options.FileProvider = new PhysicalFileProvider(#"xxx");
options.RequestPath = "/images";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStaticFiles(app.ApplicationServices.GetRequiredService<IOptions<StaticFileOptions>>().Value);
}
}
And then access it by IOptions<StaticFileOptions> like
public class HomeController : Controller
{
private readonly StaticFileOptions _options;
public HomeController(IOptions<StaticFileOptions> options)
{
this.configuration = configuration;
_serviceProvider = serviceProvider;
_options = options.Value;
}
public IActionResult Index()
{
return Ok(_options.RequestPath);
}
}

Unable to resolve service for type 'System.String' while attempting to activate 'BuySell_20190423.Controllers.StockController'

I have made many additions to this backend. Now the basic HTTPPOST from values controller copied to the stocks controller gives me this error in the chome window:
InvalidOperationException: Unable to resolve service for type
system.String' while attempting to activate
BuySell_20190423.Controllers.StockController'.
This is my stock controller post:
[HttpPost]
public void Post([FromBody]string value)
{
}
In postman I get the 500 internal error using this body,
{"UserName":"johndoe"}
Here is some of my startup.cs
namespace BuySell_20190423
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public static IConfiguration Configuration { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorsPolicy", corsBuilder =>
{
corsBuilder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
services.AddDbContext<Helpers.DataContext>(x => x.UseInMemoryDatabase("TestDb"));
services.AddDbContext<Models.StockContext>(opt => opt.UseInMemoryDatabase("item"));
services.AddDbContext<AppDbContext>(x => x.UseInMemoryDatabase("AppDb"));
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
//services.AddScoped<Post>(_ => new MyService("value here"));
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure DI for application services
services.AddScoped<IUserService, UserService>();
// configure DI for string services
services.AddHttpClient();
app.UseCors("CorsPolicy");
app.UseHttpsRedirection();
app.UseMvc();
}
}
Here is the stocks controller constructor:
public StockController(String context, IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
_context = context;
}
You have to remove the string context parameter, the dependency injection system can't know what that should be.