HTTP self-hosting and unit tests - nunit

I am working on a set of unit tests, which include testing of HTTP client/server functionality, with a self hosted server. But I can't get even the simplest test to work. HEre is my code
UnitTest1.cs
using System;
using System.Net.Http;
using System.Web.Http.SelfHost;
using NUnit.Framework;
using SomeWebService;
namespace UnitTestProject1
{
[TestFixture]
public class UnitTest1
{
[Test]
public void TestMethod1()
{
var baseAddress = new Uri("http://localhost:9876");
var config = new HttpSelfHostConfiguration(baseAddress);
new Bootstrap().Configure(config);
var server = new HttpSelfHostServer(config);
using (var client = new HttpClient(server))
{
client.BaseAddress = baseAddress;
var response = client.GetAsync("").Result;
Assert.True(response.IsSuccessStatusCode, "Actual status code: " + response.StatusCode);
}
}
}
}
Bootstrap.cs
using System.Web.Http;
namespace SomeWebService
{
public class Bootstrap
{
public void Configure(HttpConfiguration config)
{
config.Routes.MapHttpRoute(name: "API Default", routeTemplate: "{controller}/{id}", defaults: new
{
controller = "Home",
id = RouteParameter.Optional
});
}
}
}
and the HomeController.cs
using System.Net.Http;
using System.Web.Http;
namespace SomeWebService
{
class HomeController:ApiController
{
public HttpResponseMessage Get()
{
return this.Request.CreateResponse();
}
}
}
The test results in:
Actual status code: NotFound
Expected: True
But was: False
What am I doing wrong?
Packages installed
Install-Package Microsoft.Net.Http -version 2.0.20710
Install-Package Microsoft.AspNet.WebApi.SelfHost -version 4.0.20918
Install-Package Microsoft.AspNet.WebApi.Core -version 4.0.20710

If you want your tests to run even faster, you can avoid the whole TCP/IP stack by using a purely in-memory host,
[Test]
public void TestMethod1()
{
var config = new HttpConfiguration();
new Bootstrap().Configure(config);
var server = new HttpServer(config);
using (var client = new HttpClient(server))
{
client.BaseAddress = baseAddress;
var response = client.GetAsync("").Result;
Assert.True(response.IsSuccessStatusCode, "Actual status code: " + response.StatusCode);
}
}

HomeController is private, because you haven't explicitly declared it public. Try making it public:
public class HomeController:ApiController
{
public HttpResponseMessage Get()
{
return this.Request.CreateResponse();
}
}

Related

I ran my program as BackgroundService but the service does not run the code

I've created program in C# Worker Service .NET 7.0. This program listening all incoming GET request and save this requests as URI and URL into Log.txt file. I used Titanium Web Proxy to listen all incoming requests from server. Program is working correctly but problem apear when im trying to run it as windows service. To do this im publishing my program into folder, next using command prompt Im creating service choosing .exe file from published folder and starting process with "sc start ServiceName" command. Service is running but its not working at all.
Here is my Program.cs code:
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Hosting;
using Serilog.Extensions.Logging;
namespace WorkerService5
{
public class Program
{
static async Task Main(string[] args)
{
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.UseWindowsService()
.UseSerilog()
.Build();
await host.RunAsync();
}
}
}
Here is my Worker.cs code:
using System.Net;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
using Serilog;
using Microsoft.Extensions.Hosting;
namespace WorkerService5
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public Worker(ILogger<Worker> logger, IHostApplicationLifetime hostApplicationLifetime)
{
_logger = logger;
_hostApplicationLifetime = hostApplicationLifetime;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () =>
{
try
{
var proxyServer = new ProxyServer(userTrustRootCertificate: false);
proxyServer.BeforeRequest += OnRequest;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true);
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
foreach (var endPoint in proxyServer.ProxyEndPoints)
_logger.LogInformation("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
// Console.ReadLine();
stoppingToken.Register(() =>
{
proxyServer.BeforeRequest -= OnRequest;
proxyServer.Stop();
});
await Task.Delay(Timeout.Infinite, stoppingToken);
}
catch (Exception)
{
throw;
}
});
private async Task OnRequest(object sender, SessionEventArgs e)
{
var filePath = #"E:\LogiusService\Logs\log.txt";
Log.Logger = new LoggerConfiguration()
.WriteTo.File(filePath, rollingInterval: RollingInterval.Day, shared: true)
.CreateLogger();
string requestedUrl = e.HttpClient.Request.Host;
Console.WriteLine("Requested URL: " + requestedUrl);
Log.Information($"Requested URL: " + requestedUrl);
string requestedUri = e.HttpClient.Request.RequestUri.AbsoluteUri;
Console.WriteLine("Requested URI: " + requestedUri);
Log.Information($"Requested URI: " + requestedUri);
}
}
}
Any ideas how can i run it as Windows Background Service?
Cheers!

OData GetById is not working on Asp.net Core 2.2

I am trying to get a single item from my list of elements and it is not working. I am using FromODataUri correctly, I am not seeing what is wrong.
I am able to hit this resource:
https://localhost:44314/odata/Inventories
but I can't get a single item: https://localhost:44314/odata/Inventories(1)
This is how my Startup.cs looks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OData.Edm;
using OdataTest.Models;
using Swashbuckle.AspNetCore.Swagger;
namespace OdataTest
{
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.AddOData();
services.AddODataQueryFilter();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Inventory Api", Version = "v1" });
});
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
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.UseSwagger();
app.UseSwaggerUI(c =>
{
// force to add another /swagger to fix issue
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Physical Inventory API");
});
app.UseHttpsRedirection();
app.UseMvc(b =>
b.MapODataServiceRoute("odata", "odata", GetEdmModel(app.ApplicationServices))
);
}
private static IEdmModel GetEdmModel(IServiceProvider serviceProvider)
{
ODataModelBuilder builder = new ODataConventionModelBuilder(serviceProvider);
builder.Namespace = "PartsInventory";
builder.ContainerName = "PartsInventoryContainer";
builder.EntitySet<InventoryOutputDto>("Inventories").EntityType
.Filter()
.Count()
.Expand()
.OrderBy()
.Page()
.Select();
return builder.GetEdmModel();
}
}
}
And my Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using OdataTest.Models;
namespace OdataTest.Controllers
{
public class PartsInventoryController : ODataController
{
List<InventoryOutputDto> list = new List<InventoryOutputDto>() {
new InventoryOutputDto {
Description = "Description1",
UserName = "user1",
EndDate = DateTime.Now,
ErrorId = "Error",
InventoryId = 1,
LocationCode = 1,
StartDate = DateTime.Now,
StatusCode = 1
},
new InventoryOutputDto {
Description = "Description1",
UserName = "user1",
EndDate = DateTime.Now,
ErrorId = "Error2",
InventoryId = 2,
LocationCode = 2,
StartDate = DateTime.Now,
StatusCode = 2
} };
[HttpGet]
[ODataRoute("Inventories")]
/// public IActionResult Inventory(InventoryInputDto inputDto)
public IActionResult GetInventories(InventoryInputDto inputDto)
{
return StatusCode(StatusCodes.Status200OK, list);
}
[HttpGet]
[ODataRoute("Inventories({key})")]
public IActionResult GetInventoryById([FromODataUri] int key)
{
var invRecord = list.FirstOrDefault(i => i.InventoryId == key);
if(invRecord == null)
{
return NotFound();
}
return Ok(invRecord);
}
}
}

Npgsql Performance

I am trying to implement Npgsql in our DAL and running into issues under heavy load. the following sample application is a decent representation of just a simple query that under heavy load, throws a 'A command is already in progress' exception. I am assuming this is due to the lack of MARS support so I also tried creating a connection each time with a using statement around each command only to have the performance become unusable. I checked that the username is indexed so that shouldn't be an issue.
Not sure what I am doing wrong here but I need some advice on how to get this performing well.
OS: Docker Container: microsoft/dotnet:2.1.301-sdk
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace npgsqlTest
{
class Program
{
static async Task Main(string[] args)
{
DAL dal = new DAL();
dal.Prepare();
var tasks = dal.Users.Select(async user =>
{
Console.WriteLine(await dal.RunTest(user));
});
await Task.WhenAll(tasks);
}
}
public class DAL
{
private static string _ConnectionString;
private NpgsqlConnection _Connection;
public List<string> Users { get; set; } = new List<string>();
public DAL()
{
_ConnectionString = $"Host=192.168.1.1;Username=admin;Port=5432;Password=password;Database=BigDB;";
_Connection = new NpgsqlConnection(_ConnectionString);
_Connection.Open();
}
public void Prepare()
{
string query = "SELECT username FROM usertable;";
using (var cmd = new NpgsqlCommand(query, _Connection))
{
var reader = cmd.ExecuteReader();
using (reader)
{
while (reader.Read())
{
Users.Add(reader[0].ToString());
}
}
}
}
public async Task<string> RunTest(string user)
{
var parameters = new Dictionary<string, Object> { { "username", user } };
var query = $"SELECT name FROM usertable WHERE username = (#username);";
var reader = await QueryAsync(query, parameters);
using (reader)
{
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var name = reader["name"];
if (!(hash is DBNull))
return (string)name;
}
}
}
return String.Empty;
}
public async Task<DbDataReader> QueryAsync(string query, Dictionary<string, Object> parameters)
{
using (var cmd = new NpgsqlCommand(query, _Connection))
{
foreach (var parameter in parameters)
{
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value == null ? DBNull.Value : parameter.Value);
}
cmd.Prepare();
return await cmd.ExecuteReaderAsync();
}
}
}
}

Web API HttpClient PutAsync returning Http 404

Im trying to send a PUT to my Web API and am struggling a bit as to how I should construct the actual Http request. Below is an integration test sample. It works fine using HttpMessageInvoker to call the Web API Put, but I want to use HttpClient in test also since that is what I'll be using in the business layer.
[TestMethod]
public void Verify_UpdateBudgetData_Http_PUT()
{
int budgetId = 1;
string appId = "DummyApp";
string userId = "Dummy";
string value = "400";
string filterJSON =
"{dimensionFilter:{\"Demo_Konto\":[\"3000\"],\"Demo_AO\":[\"200\"]},valueSpreadType:{\"Value1\":0}}";
HttpConfiguration config = new HttpConfiguration();
Konstrukt.SL.AggregationEngine.WebApiConfig.Register(config, new SL.AggregationEngine.AutofacStandardModule());
HttpServer server = new HttpServer(config);
/*this works*/
using (HttpMessageInvoker client = new HttpMessageInvoker(server))
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put,
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON)))
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Wrong http status returned");
}
};
/*this does not work*/
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri("http://localhost");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage =
client.PutAsync(
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON), new StringContent("")).Result;
Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode, "Wrong http status returned");
}
}
Here is my WebApiConfig-class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config, Autofac.Module moduleToAppend)
{
config.Routes.MapHttpRoute(
name: "UpdateBudgetData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{appId}/{userId}/{value}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetBudgetAndRefData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{userId}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.EnableCors();
config.EnableSystemDiagnosticsTracing();
// Autofac container
// if not configured here you'll not have dependencies provided to your WebApiControllers when called
var builder = new ContainerBuilder(); // yes, it is a different container here
builder.RegisterAssemblyTypes( // register Web API Controllers
Assembly.GetExecutingAssembly())
.Where(t =>
!t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
.InstancePerLifetimeScope();
// register your graph - shared
builder.RegisterModule(
new AutofacStandardModule()); // same as with ASP.NET MVC Controllers
if (moduleToAppend != null)
{
builder.RegisterModule(moduleToAppend);
}
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(
container);
}
public static void Register(HttpConfiguration config)
{
Register(config, null);
}
}
How can I fix the HttpClient call to PutAsync? Should I embed the FilterJSON parameter in the body? If so, how to do that? I've tried that but then the FromBody parametger was null...
I got it working by using the FromBody tag in the controller and then wrapping that parameter in the http request body. An important note to is to prefix the parameter with an "=" sign to make sure it was interpreted correctly by the controller. Also I removed the same parameter from the route config. Finally to make the client to server request work I had to replace HttpServer Class with httpselfhostserver

Using Autofac with webapi and mvc5.1 not working for webapi

I have a project using both mvc and webapi.
It's a Membership Reboot application so I have taken the example single application project and have slightly modified it to suit.
The DI works ok for controllers however when I try to call a webapi controller I keep getting an error
Make sure that the controller has a parameterless public constructor.
Is there something else I need to do for using autofac with webapi?
This is the code from my startup.cs
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive
});
ConfigureMembershipReboot(app);
}
private static void ConfigureMembershipReboot(IAppBuilder app)
{
System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
//System.Data.Entity.Database.SetInitializer(new System.Data.Entity.CreateDatabaseIfNotExists<DefaultMembershipRebootDatabase>());
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = MembershipRebootOwinConstants.AuthenticationType
};
BuildAutofacContainer(app, cookieOptions.AuthenticationType);
app.UseMembershipReboot(cookieOptions);
}
private static void BuildAutofacContainer(IAppBuilder app, string authType)
{
var builder = new ContainerBuilder();
var config = CreateMembershipRebootConfiguration(app);
builder.RegisterInstance(config).As<MembershipRebootConfiguration>();
builder.RegisterType<DefaultUserAccountRepository>()
.As<IUserAccountRepository>()
.As<IUserAccountQuery>()
.InstancePerLifetimeScope();
builder.RegisterType<UserAccountService>().OnActivating(e =>
{
var owin = e.Context.Resolve<IOwinContext>();
var debugging = false;
#if DEBUG
debugging = true;
#endif
e.Instance.ConfigureTwoFactorAuthenticationCookies(owin.Environment, debugging);
})
.AsSelf()
.InstancePerLifetimeScope();
builder.Register(ctx =>
{
var owin = ctx.Resolve<IOwinContext>();
return new OwinAuthenticationService(authType, ctx.Resolve<UserAccountService>(), owin.Environment);
})
.As<AuthenticationService>()
.InstancePerLifetimeScope();
builder.Register(ctx=>HttpContext.Current.GetOwinContext()).As<IOwinContext>();
builder.RegisterControllers(typeof(Startup).Assembly);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
It was a 1 liner :)
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);