Web API application does not run any controller code - c#

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.

Related

Implementing Azure Event Grid event handler in a Web API which subscribes to changes in Azure App Configuration

I am exploring various methods to get configuration data from Azure App Configuration and got most methods working however I am struggling to implement the Azure Event Grid event handler in a Web API on ASP.NET Core 3.1. I want to know if it's possible to notify the Web API about changes through the Event Grid instead of it polling when its cache set up for Azure App configuration has expired. Once notified, the configuration values should update and the new values should feed to any controllers when someone makes a request to the Web API.
I have included the contents in my Program.cs, Startup.cs, service bus consumer where I set up the event grid subscriber and a sample controller.
From what I've read from Microsoft's documentation about this, my understanding is that the IConfigurationRefresher.ProcessPushNotification method resets the cache expiration to a short random delay rather the cache expiration set in the CreateHostedBuilder method and when the IConfigurationRefresher.TryRefreshAsync() is called it updates the configuration values.
The issue I am having is not being able to inject an instance of the concrete class for IConfigurationRefresher to the RegisterRefreshEventHandler method and in turn call ProcessPushNotification to reset the expiration time.
I may also be going about this wrongly as I'm assuming the RegisterRefreshEventHandler code which works in a console application will work for a Web API as well. Please let me know if my approach will work?
Program.cs
public class Program
{
private static IConfiguration _configuration;
private static IConfigurationRefresher _refresher;
public static void Main(string[] args)
{
_configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: false).Build();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
var settings = config.Build();
config.AddAzureAppConfiguration(options =>
{
options
.Connect(_configuration["AppConfig"]).ConfigureRefresh(
refresh => refresh.Register(key: "Api:Sentinel", refreshAll: true).SetCacheExpiration(TimeSpan.FromDays(1))
).Select(KeyFilter.Any, "ClientA");
_refresher = options.GetRefresher();
}
);
}).UseStartup<Startup>());
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration, IConfigurationRefresher configurationRefresher)
{
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.Configure<TFASettings>(Configuration.GetSection("Api:TFA"));
services.AddControllers();
services.AddAzureAppConfiguration();
services.AddSingleton<IServiceBusConsumer, ServiceBusConsumer>();
}
// 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();
}
var bus = app.ApplicationServices.GetRequiredService<IServiceBusConsumer>();
bus.RegisterRefreshEventHandler();
app.UseAzureAppConfiguration();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
ServiceBusConsumer.cs
public class ServiceBusConsumer : IServiceBusConsumer
{
public IConfigurationRefresher configurationRefresher;
public ServiceBusConsumer()
{
}
public void RegisterRefreshEventHandler()
{
SubscriptionClient serviceBusClient = new SubscriptionClient("*****", "*****", "*****");
serviceBusClient.RegisterMessageHandler(
handler: (message, cancellationToken) =>
{
// Build EventGridEvent from notification message
EventGridEvent eventGridEvent = EventGridEvent.Parse(BinaryData.FromBytes(message.Body));
// Create PushNotification from eventGridEvent
eventGridEvent.TryCreatePushNotification(out PushNotification pushNotification);
//// Prompt Configuration Refresh based on the PushNotification
//configurationRefresher.ProcessPushNotification(pushNotification);
return Task.CompletedTask;
},
exceptionReceivedHandler: (exceptionargs) =>
{
Console.WriteLine($"{exceptionargs.Exception}");
return Task.CompletedTask;
});
}
}
Sample controller
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly TFASettings _tfaSettings;
public WeatherForecastController(IOptionsSnapshot<TFASettings> tfaSettings)
{
_tfaSettings = tfaSettings.Value;
}
[HttpGet]
public WeatherForecast Get()
{
return new WeatherForecast
{
AuthenticationText = _tfaSettings.AuthenticationWording
};
}
}
You can get the concrete instance of IConfigurationRefresher through dependency injection. You can even call RegisterRefreshEventHandler() from the constructor too. Your ServiceBusConsumer.cs can look something like this.
private IConfigurationRefresher _configurationRefresher;
public ServiceBusConsumer(IConfigurationRefresherProvider refresherProvider)
{
_configurationRefresher = refresherProvider.Refreshers.FirstOrDefault();
RegisterRefreshEventHandler();
}

MassTransit Azure Service Bus Request-Response ASP.NET Core RequestTimeoutException

My goal is to make the communication between two applications (WebAPI and Worker) via MassTransit's Request/Response technique. The problem is that I'm never getting inside the consumer (request client), I'm getting a timeout instead.
I found a similar question already but the answer included a link to a github repository which no longer exists. I also tried following a sample but for some reason most samples are created as console applications which is useless for me since I have two WebAPIs trying to communicate with each other.
Anyway, here's my code:
WebAPI.Startup
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMassTransit(massTransitConfig =>
{
massTransitConfig.UsingAzureServiceBus((ctx, cfg) =>
{
cfg.Host("Endpoint=sb://----.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=----");
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Worker.Startup
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMassTransit(massTransitConfig =>
{
massTransitConfig.UsingAzureServiceBus((ctx, cfg) =>
{
cfg.Host("Endpoint=sb://----.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=----");
});
massTransitConfig.AddConsumer<CreateScheduleRequestClient>();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
WebAPI.RequestController
[Route("api/requests")]
public class RequestsController : ControllerBase
{
private readonly IBus _bus;
public RequestsController(IBus bus)
{
_bus = bus;
}
[HttpPost("create-schedule")]
public async Task<IActionResult> CreateSchedule()
{
var client = _bus.CreateRequestClient<CreateScheduleRequest>();
var response = await client.GetResponse<ScheduleCreatedResponse>(new CreateScheduleRequest());
return Ok(response.Message.Succeeded);
}
}
DataTransferObjects.CreateScheduleRequest
public class CreateScheduleRequest
{
public string CommandName { get; set; }
public string Cron { get; set; }
}
Worker.RequestClients.CreateScheduleRequestClient
public class CreateScheduleRequestClient : IConsumer<CreateScheduleRequest>
{
public async Task Consume(ConsumeContext<CreateScheduleRequest> context)
{
await context.RespondAsync(new ScheduleCreatedResponse(true));
}
}
DataTransferObjects.ScheduleCreatedResponse
public class ScheduleCreatedResponse
{
public bool Succeeded { get; }
public ScheduleCreatedResponse(bool succeeded)
{
Succeeded = succeeded;
}
}
When I call the only endpoint in the RequestsController, I'm getting MassTransit.RequestTimeoutException: Timeout waiting for response, RequestId: 27f60000-167b-00ff-ea0f-08d8e3a0832e after a short period. I'm not able to verify much more about it, I thought it outght to work out of the box but perhaps I'm missing some parameters when initializing the bus in my Startup classes?
================EDIT================
I changed my code a little with regards to what Chris Patterson suggested and to specify that I'd like to go with the IBus approach. The code still throws the same exception after the change.
First, I'd suggest reviewing the request documentation, in particular the controller example that injects IRequestClient<T> into the controller. That will fix your controller code, which shouldn't be using IBus.
Second, your response should be an actual message type, it can't be true. You need to create a second message contract, such as ScheduleRequestCreated and respond with that message type. Then, your GetResponse would change to
GetResponse<ScheduleRequestCreated>(new CreateScheduleRequest(...))
And your response would be:
RespondAsync(new ScheduleRequestCreated(...))

Working with Webhost and Webhook receivers in a unitTest [.net]

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" }

Connecting to remote Jaegertracing with C# .Net Core

Im having some issues here with Opentracing and Jaegertracing when it comes to C#. I have had this working before, but with Java projects. So I start to wonder what Im missing when it comes to C# .NET Core web service.
This is my class to start my tracer to be used
public static class MyTracer
{
public static ITracer tracer = null;
public static ITracer InitTracer()
{
Environment.SetEnvironmentVariable("JAEGER_SERVICE_NAME", "my-store");
Environment.SetEnvironmentVariable("JAEGER_AGENT_HOST", "192.168.2.27");
Environment.SetEnvironmentVariable("JAEGER_AGENT_PORT", "6831");
Environment.SetEnvironmentVariable("JAEGER_SAMPLER_TYPE", "const");
Environment.SetEnvironmentVariable("JAEGER_REPORTER_LOG_SPANS", "false");
Environment.SetEnvironmentVariable("JAEGER_SAMPLER_PARAM","1");
Environment.SetEnvironmentVariable("JAEGER_SAMPLER_MANAGER_HOST_PORT", "5778");
Environment.SetEnvironmentVariable("JAEGER_REPORTER_FLUSH_INTERVAL" , "1000");
Environment.SetEnvironmentVariable("JAEGER_REPORTER_MAX_QUEUE_SIZE" , "100");
var loggerFactory = new LoggerFactory();
var config = Configuration.FromEnv(loggerFactory);
tracer = config.GetTracer();
if (!GlobalTracer.IsRegistered())
{
GlobalTracer.Register(tracer);
}
return tracer;
}
}
Controller code that should report to the Jaeger agent and collector for show in the UI.
[Route("api/[controller]")]
[ApiController]
public class ComponentController : ControllerBase
{
private readonly ITracer tracer;
public ComponentController(ITracer tracer)
{
this.tracer = tracer;
}
/// <summary>
/// Get component by ID
/// </summary>
/// <returns></returns>
[HttpGet("GetComponent")]
public ActionResult<ComponentModel> GetComponent(string id)
{
var builder = tracer.BuildSpan("operationName");
var span = builder.Start();
// Set some context data
span.Log("Getting data");
span.SetTag(Tags.SpanKind, "Getting data request");
span.Finish();
ComponentModel component = ComponentManager.GetComponent(id);
return component;
}
}
Startup.cs
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();
// Use "OpenTracing.Contrib.NetCore" to automatically generate spans for ASP.NET Core, Entity Framework Core, ...
// See https://github.com/opentracing-contrib/csharp-netcore for details.
services.AddOpenTracing();
//Init tracer
services.AddSingleton<ITracer>(t => MyTracer.InitTracer());
services.AddHealthChecks();
}
// 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();
});
}
}
But this is not working at all. What am I missing here to get it work with a remote server?
Iv finally found the solution. It seemed to have to do with how the reporter is started up. Anyhow, I changed my tracer class to this.
public static class MyTracer
{
public static ITracer tracer = null;
public static ITracer InitTracer(IServiceProvider serviceProvider)
{
string serviceName = serviceProvider.GetRequiredService<IHostingEnvironment>().ApplicationName;
Environment.SetEnvironmentVariable("JAEGER_SERVICE_NAME", "my-store");
//Environment.SetEnvironmentVariable("JAEGER_AGENT_HOST", "192.168.2.27");
//Environment.SetEnvironmentVariable("JAEGER_AGENT_PORT", "6831");
//Environment.SetEnvironmentVariable("JAEGER_SAMPLER_TYPE", "const");
//Environment.SetEnvironmentVariable("JAEGER_REPORTER_LOG_SPANS", "false");
//Environment.SetEnvironmentVariable("JAEGER_SAMPLER_PARAM","1");
//Environment.SetEnvironmentVariable("JAEGER_SAMPLER_MANAGER_HOST_PORT", "5778");
//Environment.SetEnvironmentVariable("JAEGER_REPORTER_FLUSH_INTERVAL" , "1000");
//Environment.SetEnvironmentVariable("JAEGER_REPORTER_MAX_QUEUE_SIZE" , "100");
//application - server - id = server - x
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var sampler = new ConstSampler(sample: true);
var reporter = new RemoteReporter.Builder()
.WithLoggerFactory(loggerFactory)
.WithSender(new UdpSender("192.168.2.27", 6831, 0))
.Build();
tracer = new Tracer.Builder(serviceName)
.WithLoggerFactory(loggerFactory)
.WithSampler(sampler)
.WithReporter(reporter)
.Build();
if (!GlobalTracer.IsRegistered())
{
GlobalTracer.Register(tracer);
}
return tracer;
}
}
I know there are several inactive variables here right now. Will see if they still can be of use some how. But none is needed right now to get it rolling.
Hope this might help someone else trying to get the .NET Core working properly together with a remote Jeagertracing server.

ASP.NET CORE Custom Action Filter Attribute Calling on Controller where it is not applied (IIS)

I am using an Custom Action Filter attribute to check if session is null or not..
Authenticate.cs
public class Authenticate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var data = context.HttpContext.Session.GetString("UserSession");
if (data == null)
{
bool isAjax = context.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
if (isAjax)
{
context.Result = new JsonResult("Session Expired!");
}
else
{
context.Result = new RedirectResult("/Account/Login");
}
}
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// our code after action executes
}
}
I am calling this attribute on all controllers except Account Controller
AccountController.cs
public class AccountController : Controller
{
UserFunctions userFunctions;
public AccountController(ARSContext context)
{
userFunctions = new UserFunctions(context);
}
public IActionResult Index()
{
return View();
}
public IActionResult Login()
{
return View();
}
[HttpPost]
public IActionResult Login(AccountViewModel viewModel)
{
User user = userFunctions.Login(viewModel.Username, viewModel.Password);
if (user != null)
{
HttpContext.Session.SetString("UserSession", JsonConvert.SerializeObject(user));
return Json(true);
}
return Json(false);
}
}
My Startup.cs
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.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddDbContext<MyDBContext>(item => item.UseSqlServer(Configuration.GetConnectionString("MyDBEntities")));
services.AddScoped<CountryFunctions>();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
app.UseDeveloperExceptionPage();
if (env.IsDevelopment())
{
}
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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Now the main problem is that all of this is working perfectly on my local computer when I run it through Visual Studio. But when I deploy it on IIS what happens is that when I call '/Account/Login' without 'HttpPOST' it shows you view.
But when I submit the form and call '/Account/Login' 'HttpPost' action then My Authenticate filter is called which redirects me back to '/Account/Login' View Action. I also tried to use ServiceFilters but same problem persists.

Categories

Resources