I want to receive a webhook from Postman within a unittest. But i've no clue how to create something like this. I've read the official documentation on webhooks (the receiving end specifically), but i find it rather vague regarding how to set it up.
I've already accomplished to set up a webhost in a unitTest, but I have no clue on how to setup the routing without controllers and actions. Let alone how to setup the webhook.
UnitTest1.cs
[TestMethod]
public void TestMethod1()
{
IWebHost myHost = WebHost.CreateDefaultBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseUrls("http://localhost:5001/")
.Build();
myHost.Run();
}
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
/* configure any services you need here */
}
public void Configure(IApplicationBuilder app)
{
// Output a "hello world" to the user who accesses the server
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello, w!");
});
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Load receivers
}
}
I've tried to simply make an HTTPOST and HTTPGET methods to receive the postman messages, but honestly i don't know how it works.
[HttpPost]
[Route("SendHere")]
public void Post(object message)
{
var receivedMSG = ($"Received webhook: {message}");
}
[HttpGet]
[Route("GetHere")]
public HttpResponseMessage Get(string echo)
{
var resp = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(echo, Encoding.UTF8, "text/plain")
};
return resp;
}
How i would i go about setting up a webhook end point from within a unittest?
Got it to work!
UnitTest1.cs
public class UnitTest1
{
private const string url = "http://localhost:5001";
[TestMethod]
public void TestMethod1()
{
CreateHostBuilder().Build().Run();
}
public static IHostBuilder CreateHostBuilder() =>
Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(url);
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Controllers/TestController.cs
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "One", "Two", "Three" };
}
}
http://localhost:5001/Test > { "One", "Two", "Three" }
Related
I've created a new ASP.NET core Web API project. But when I run it in my development environment it is giving me "HTTP Error 404. This localhost page can't be found.". I tried debugging the application by placing several debug points in Program.cs and ServiceExtension.cs class, but the program control doesn't seem to enter the Controller classes and Service classes.
My program.cs is as below:
var builder = WebApplication.CreateBuilder(args);
// Including the path for configuration file for the NLog configuration.
LogManager.LoadConfiguration(string.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
// Add services to the container.
builder.Services.ConfigureCors();
builder.Services.ConfigureIISIntegration();
builder.Services.ConfigureLoggerService();
builder.Services.ConfigureSqlContext(builder.Configuration);
builder.Services.ConfigureRepositoryManager();
builder.Services.ConfigureServiceManager();
builder.Services.AddControllers().AddApplicationPart(typeof(CompanyEmployees.Presentation.AssemblyReference).Assembly);
var app = builder.Build();
if(app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All });
app.UseCors("CorsPolicy");
app.UseAuthorization();
app.MapControllers();
app.Run();
The ServiceExtension.cs class is below:
namespace CompanyEmployees.Extensions
{
public static class ServiceExtensions
{
public static void ConfigureCors(this IServiceCollection services) =>
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
public static void ConfigureIISIntegration(this IServiceCollection services) =>
services.Configure<IISOptions>(options =>
{
//We are not adding any options becasue we are configuring for default ones
});
public static void ConfigureLoggerService(this IServiceCollection services) => services.AddSingleton<ILoggerManager, LoggerManager>();
public static void ConfigureRepositoryManager(this IServiceCollection services) => services.AddScoped<IRepositoryManager, RepositoryManager>();
public static void ConfigureServiceManager(this IServiceCollection services) => services.AddScoped<IServiceManager, ServiceManager>();
public static void ConfigureSqlContext(this IServiceCollection services, IConfiguration configuration) => services.AddDbContext<RepositoryContext>(opts => opts.UseSqlServer(configuration.GetConnectionString("sqlconnection")));
}
}
The only controller class is as below:
namespace CompanyEmployees.Presentation.Controllers
{
[Route("api/companies")]
[ApiController]
public class CompaniesController : ControllerBase
{
private readonly IServiceManager _serviceManager;
public CompaniesController(IServiceManager serviceManager)
{
_serviceManager = serviceManager;
}
[HttpGet]
public IActionResult GetCompanies()
{
try
{
var companies = _serviceManager.CompanyService.GetAllCompanies(trackChanges: false);
return Ok(companies);
}
catch
{
return StatusCode(500, "Internal Server Error");
}
}
}
}
Does anyone know what is going wrong here? or how can I effectively debug the solution?
you have decorated you controller with
[Route("api/companies")]
but in your call you are not including "api".
just call
localhost:5000/api/companies
I already have been trying to solve this for days, I created a Web API application (using .NET 5, VS2019) and when I was finally be able to run it without errors in the IIS, I receive 404 errors in the responses, simply put the controller code is never called (never hit by the debugger) with no exceptions or any other errors, just this screen (calling the first route in the controller):
This localhost page can’t be found
No webpage was found for the web address:
https://localhost:44342/sicrestweb/sync/gs/usuarios?json_data={fields:*,%20database:"Provider=Microsoft.ACE.OLEDB.12.0;Data%20Source=F:\VBDev\Sicrest3.4\DPWT314.mdb;%20Persist%20Security%20Info=False;"}
Here is the Program code:
public static void Main(string[] args)
{
CD = Directory.GetCurrentDirectory();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
The 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<ISyncService, SyncService>();
}
// 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(); });
}
}
The controller code (fragment) :
[ApiController]
[Route("SicrestWeb/[controller]")]
public class SyncController : ControllerBase // Controller
{
private readonly ISyncService ISS;
private SyncService SS { get { return (SyncService) ISS; } }
public ActionResult HTTPResponse { get { return SS.HTTPResponse; } } // HTTP Response
public String CurrentRoute { get { return (Request != null && Request.Path != null) ? (Request.Path.Value ?? TestRoute) :TestRoute; } } // Get route string
public String TestRoute { get; set; }
public SyncController(ISyncService isys) { ISS = isys; TestRoute = ""; }
[HttpGet]
[Route("GS/{table_name}/{json_data}/{key=''}")]
[Route("GSI/{table_name}/{json_data}/{key=''}")]
public ActionResult Select(String table_name, String json_data, String key="") // (GS) returns data as requested
{
// process request
if (!ISS.Dispatch(CurrentRoute, table_name, json_data, key))
{
// Handles error
}
// Generate response
return HTTPResponse;
}
.
.
.
}
The only things I am sure is the controller code works when is called directly (it is already unit tested), but the address/routing mechanism never calls it, like the controller and its routes are not identified thus are never called.
I have a .net core 3.1 background application where i am spanning kestrel server as per implementation.
What i need is to initialize some property (let's say int channel id) when it is configured at startup
Startup
internal class Startup
{
internal static IHostBuilder CreateHostBuilder(MyConfigurations _objSettings)
{
//_objSettings.channelId need to be assigned to CallBackController channelID
return Host
.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.UseUrls(_objSettings.CallbackLocalListener)
)
.UseStartup<Startup>());
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options => options.RespectBrowserAcceptHeader = true);
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseForwardedHeaders();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
}
Controller
[Produces("application/json")]
public class CallBackController : ControllerBase
{
int channelId = 0;//this need to be initializes
public IActionResult Receive()
{
IActionResult result = null;
return result;
}
}
I needed this as same class multiple instances will be starting kestrel instance on other ports listening different traffic. At the time of request receive i need that channel Id to initialize something
Create an type to store the desired options
public class ChannelOptions {
public int ChannelId { get; set; }
}
and configure that in Startup using the convivence members
internal static IHostBuilder CreateHostBuilder(MyConfigurations _objSettings) {
return Host
.CreateDefaultBuilder()
.ConfigureServices(services =>
services.AddSingleton(
new ChannelOptions {
ChannelId = _objSettings.channelID
}
)
)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.UseUrls(_objSettings.CallbackLocalListener)
)
.UseStartup<Startup>());
}
and finally, explicitly inject the options into the controller as a dependency
[Produces("application/json")]
public class CallBackController : ControllerBase {
private readonly int channelId = 0;
public CallBackController(ChannelOptions options) {
channelId = options?.ChannelId ?? 0;
}
public IActionResult Receive() {
IActionResult result = null;
return result;
}
//...
}
How to remove CORS restriction for one Controller Action
I have implemented CORS for one of my application for all controllers/ all action at one place.
But don't know how to remove this restriction for just single controller
My code for one other place is
public static IWebHostBuilder BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args).
ConfigureKestrel(serverOptions =>
{
}).UseIISIntegration()
.UseStartup<StartupShutdownHandler>();
private const string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public StartupShutdownHandler(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
CorsRelatedPolicyAddition(services);
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
}); }
private void CorsRelatedPolicyAddition(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.AllowedAnyOrigins().AllowAnyMethod().AllowAnyHeader(); });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
{...
app.UseCors(MyAllowSpecificOrigins);
..
}
For entire controller (all the method in it)
[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
For a particular method only
[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public HttpResponseMessage GetItem(int id) { ... }
For more detail see this link
I was working with KOA 2.0 and started to test asp.net core. But can't find a way to handle request/url specific middleware
Say in Koa, with router I can achieve the below:
.post("/api/v1/user", Middleware1, Middleware2, RequestValidationMiddleware, SpecificAction);
.get("/api/v1/user", Middleware1, Middleware2, RequestValidationMiddleware, SpecificAction1);
.post("/api/v1/role", Middleware1, Middleware4, RequestValidationMiddleware2, SpecificAction2);
How to achieve it with asp.net core?
Tried the below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//app.UseApiLog();
app.Map("/api", ApiLogApps);
app.Map("/exlog", ExceptionLogApps);
//app.UseMvc(routes =>
//{
// routes.MapRoute(
// name: "default",
// template: "apilog/{controller}/{action}");
// routes.MapRoute(
// name: "default2",
// template: "exlog/{controller=Home}/{action=Index}/{id:int}");
//});
}
private static void ApiLogApps(IApplicationBuilder app)
{
//app.Run(() => )
app.UseApiLog();
app.UseMvc();
}
And in controller I have
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("test/get/{id}")]
public string Get(int id)
{
return "value";
}
}
But I am lost here.
What I want is, I want to have DataValidation to be handled in a middleware - that forces me to have per url (almost) specific middleware.
PS - I know, model validation can be done in action, but I don't want that.
Thanks in advance for your help :)
To use middlewares like Koa2 , you can configure a sequence of middlewares to build a route :
public IRouter BuildRouter(IApplicationBuilder applicationBuilder)
{
var builder = new RouteBuilder(applicationBuilder);
// use middlewares to configure a route
builder.MapMiddlewareGet("/api/v1/user", appBuilder => {
// your Middleware1
appBuilder.Use(Middleware1);
appBuilder.Use(Middleware2);
appBuilder.Use(RequestValidationMiddleware);
appBuilder.Run(SpecificAction);
});
builder.MapMiddlewarePost("/api/v1/user", appBuilder => {
// your Middleware1
appBuilder.Use(Middleware1);
appBuilder.Use(Middleware2);
appBuilder.Use(RequestValidationMiddleware);
appBuilder.Run(SpecificAction1);
});
// ....
return builder.Build();
}
and then use RouterMiddleware via UseRouter(router) :
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ...
app.UseRouter(BuildRouter(app));
// ...
app.UseMvc();
}
a screenshot:
[Update]:
To integrate with attribute routing, just add a UseMvc() insteand of Run() as below :
public IRouter BuildRouter(IApplicationBuilder applicationBuilder)
{
var builder = new RouteBuilder(applicationBuilder);
// use middlewares to configure a route
builder.MapMiddlewareGet("/api/v1/user", appBuilder => {
appBuilder.Use(Middleware1);
appBuilder.Use(Middleware2);
appBuilder.Use(RequestValidationMiddleware);
appBuilder.UseMvc(); // use a MVC here ...
});
builder.MapMiddlewarePost("/api/v1/user", appBuilder => {
appBuilder.Use(Middleware1);
appBuilder.Use(Middleware2);
appBuilder.Use(RequestValidationMiddleware);
appBuilder.UseMvc();
});
// ....
return builder.Build();
}
Just for a demo , the Middleware1 is a dummy middleware that adds a HttpContext.Items['mw-message1']:
private Func<RequestDelegate, RequestDelegate> Middleware1 = next=> {
return async context =>
{
context.Items["mw-message1"] = "mw1";
await next(context);
};
};
the Controller is a plain controller that will retrieve the HttpContext.Items["mw-messages1"]:
[Route("api/v1/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
public IActionResult Index()
{
var x = (string)HttpContext.Items["mw-message1"];
return new JsonResult(new {
MW1 = x,
Hello= "Hello",
});
}
}
and now , when make a Get request to /api/v1/user , the final response is :
{"mW1":"mw1","hello":"Hello"}
I am using below code in for .net core 3 webapi.
add below in startup.cs configure method to add middleware for specific route
//Middleware to check Authorization key is present in the request header
//Map to all routes
_ = app.UseHeaderKeyAuthorization();
//Map to all routes
_ = app.MapWhen(context => context.Request.Path.StartsWithSegments("/api/v1.0"), appBuilder =>
{
_ = appBuilder.UseHeaderKeyAuthorization();
});
create middleware HeaderKeyAuthorizationMiddleware, sample below
//HeaderKeyAuthorizationMiddleware
public class HeaderKeyAuthorizationMiddleware
{
private readonly RequestDelegate next;
public HeaderKeyAuthorizationMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext httpContext)
{
var authHeader = httpContext.Request.Headers[ApiConstants.AuthHeaderName];
//check authHeader logic
if (!string.IsNullOrEmpty(authHeader))
{
await next.Invoke(httpContext);
return;
}
//Reject request if there is no authorization header or if it is not valid
httpContext.Response.StatusCode = 401;
await httpContext.Response.WriteAsync("Unauthorized");
}
}
//Middleware extension to register middleware
public static class HeaderKeyAuthorizationMiddlewareExtension
{
public static IApplicationBuilder UseHeaderKeyAuthorization(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<HeaderKeyAuthorizationMiddleware>();
}
}