Intercommunication between asp.net core web Api using RabbitMQ - rabbitmq

I have two web api.
One publish a message when specific controller be call (work)
One subscribe to receive message. (not work)
Here is config for both web api :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddRawRabbit();
var builder = new ContainerBuilder();
builder.RegisterRawRabbit("guest:guest#localhost:15672/");
var container = builder.Build();
}
In web api 1, here is action that publish a message :
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
EventMessage message = new EventMessage() ;
var client = BusClientFactory.CreateDefault();
message = new EventMessage() { id = new Guid(), createDate = DateTime.Now };
client.PublishAsync<EventMessage>(message);
Console.WriteLine($"message create {message.ToString()}");
return new string[] { "value1", "value2",};
}
}
And in web api 2, I dont know how to receive that message,
here is the way that i try but it not work
public class Listener
{
public static void Start()
{
var client = BusClientFactory.CreateDefault());
client.SubscribeAsync<EventMessage>(async (msg, context) => Console.WriteLine($"Recieved:{msg.createDate.ToString()}.");
}
}
public class Program
{
public static void Main(string[] args)
{
//not working
Listener.Start();
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
In this project, im using Rawrabbit, any library with the same scenario will be ok. Thanks
Update: i fix this code, it work

Related

Masstransit and RabbitMQ to Console Application (use Asp.net Core Web API)

I created a Solution with ASP.NET Core WEB API project, some class libraries (Domain, DI and ect), and a console application.
A console application that I use as a RabbitMQ Consumer with Masstransit library it should take messages from RabbitMQ (I have Producer project and it sends for RabbitMQ messages without problems)
My ConsoleApplication:
like this Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(x =>
{
x.AddConsumer<MessageConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
var connectionString = new Uri("RabbitMQ_URL");
cfg.Host(connectionString);
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService(true);
services.AddHostedService<Worker>();
});
}
With Worker.cs:
public class Worker : BackgroundService
{
readonly IBus _bus;
public Worker(IBus bus)
{
_bus = bus;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var factory = new ConnectionFactory() { Uri = new
Uri("RabbitMQ_URL"), DispatchConsumersAsync = true };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "MessageQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.Received += async (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var #event = JsonConvert.DeserializeObject<Event>
(message);
await _bus.Publish(new Event { DataJson = #event });
await Task.Yield();
};
channel.BasicConsume(queue: "MessageQueue",
autoAck: true,
consumer: consumer);
_logger.LogInformation("Received Text: {Text}", context.Message.DataJson);
return Task.CompletedTask;
}
}
}
MessageConsumer.cs:
public class MessageConsumer :
IConsumer<Event>
{
readonly ILogger<MessageConsumer> _logger;
public MessageConsumer(ILogger<MessageConsumer> logger)
{
_logger = logger;
}
public Task Consume(ConsumeContext<Event> context)
{
_logger.LogInInformation("Recieved Text: {Text},
context.Message.DataJson");
return Task.CompletedTask;
}
}
And my Event.cs:
public class Event
{
public ServiceType ServiceType { get; set; }
public string DataJson { get; set; }
}
public enum ServiceType
{
ComplareSitter
}
Please help me
Thanks a lot.
You might start with a clean and simple worker service using one of the MassTransit templates, just to verify your setup/configuration. There is a video available showing how to setup and use the templates.
But an obvious question, why on earth are you connecting to RabbitMQ and creating a basic consumer inside the Consume method? The message has already been deserialized as your Event type and is ready to be used. There is absolutely no need to use any part of the RabbitMQ Client library in your application when using MassTransit.

How to have a Self Hosting signalR server running as as NetCore Console App

I would like to create a SignalR Self hosting Server within a console app using .NetCore.
I am completely new to web development and .Net Core but would like to use SignalR as a real-time web based protocol. No web page is required, and so I would like to have a console app.
I have successfully tested the .Net Framework example below and would like to replicate this using .Net Core 3.1, so that it can run on Linux. However I cannot find any suitable examples.
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
using Microsoft.Owin.Cors;
namespace SignalRSelfHost
{
class Program
{
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8088";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
Clients.All.addMessage(name, "World");
}
}
}
In an attempt to use Owin to create a server console app I have the following code and this compiles, however complains about no server service being registered when I run the program. Could someone please advise what to add to have a web server without web page? The example I copied specified UseKestrel() but I think this is for a web page, so I think I need something else.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace OwinConsole
{
public class Startup
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
app.UseOwin(pipeline =>
{
pipeline(next => OwinHello);
});
}
public Task OwinHello(IDictionary<string, object> environment)
{
string responseText = "Hello World via OWIN";
byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);
// OWIN Environment Keys: http://owin.org/spec/spec/owin-1.0.0.html
var responseStream = (Stream)environment["owin.ResponseBody"];
var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
responseHeaders["Content-Type"] = new string[] { "text/plain" };
return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
}
using System.IO;
using Microsoft.AspNetCore.Hosting;
namespace OwinConsole
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
Thanks.
For those who want to achive this in .NET 6:
To create a simple server as a console application, you have to create a new empty ASP.NET Core project. In .NET 6 you don't need the 'startup.cs' anymore. You just need to change a few things in 'Program.cs' to configure SignalR.
Program.cs
var builder = WebApplication.CreateBuilder(args);
//builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
Add a Hub
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
Console.WriteLine("Received message, sending back echo");
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Client (console application)
For example:
using Microsoft.AspNetCore.SignalR.Client;
namespace Client
{
public class Program
{
private static HubConnection _connection;
public static async Task Main(string[] args)
{
_connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7116/chatHub")
.Build();
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
await ConnectAsync();
bool stop = false;
while (!stop)
{
Console.WriteLine("Press any key to send message to server and receive echo");
Console.ReadKey();
Send("testuser", "msg");
Console.WriteLine("Press q to quit or anything else to resume");
var key = Console.ReadLine();
if (key == "q") stop = true;
}
}
private static async Task ConnectAsync()
{
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Console.WriteLine("Received message");
Console.WriteLine($"user: {user}");
Console.WriteLine($"message: {message}");
});
try
{
await _connection.StartAsync();
}
catch(Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
}
private static async void Send(string user, string msg)
{
try
{
await _connection.InvokeAsync("SendMessage", user, msg);
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e}");
}
}
}
}
The client will connect to the server, after that in a loop you can send a message to the server by pressing any key and the server will send you the same message back.
In 'launchSettings.json' (Server) you can find the applicaitonUrl
As mentioned by Noah, my solution was based on
[https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-5.0&tabs=visual-studio]
But instead was built as a console app referencing Microsoft.AspNetCore.App (2.2.8).
ChatHub
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRServer
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
Startup
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
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.AddRazorPages();
services.AddSignalR();
}
// 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("/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.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
}
}
Program
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
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>();
webBuilder.UseUrls("http://localhost:2803");
});
}
}

Do I need to register autofac's container with itself to wire up webapi's depdendency resolver?

I am writing a Windows service using Topshelf that should start a self hosted webapi project and a FIX service based on quickfix/n. Please consider the shortened code below, which works so far.
However there is one problem - there are now two container instances living in my application. My guts tell me this is a bad idea, especially because I am loading MyBigModule two times. Also because one of my controllers require the same component than the one using quickfix.
// Assembly A referencing B
public class Program
{
public static void Main(string[] args)
{
var builder = new ContainerBuilder();
buider.RegisterModule<MyBigModule>();
var container = builder.Build();
_ = HostFactory.Run(c =>
{
c.UseAutofacContainer(container);
c.Service<IMyServiceManager>(svc =>
{
svc.ConstructUsingAutofacContainer();
// ...
}
// ...
});
}
}
// Assembly B
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// ...
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
// Assembly B
public class WebHost : IWebHost
{
// ...
public void Start()
{
WebApp.Start<Startup>("someUrl");
}
}
// Assembly B
public class MyBigModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<WebHost>.As<IWebHost>();
// ...
}
}
My first approach was to pass an Action<IAppBuilder> to the WebHost constructor, that is created within Main(). Something like this:
public class Program
{
public static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyBigModule>();
var container = builder.Build();
var webhost = new WebHost("someUrl", app =>
{
var config = new HttpConfiguration();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// ....
});
builder.RegisterInstance(webost);
// ...
}
}
However I would have to build my container first and then add another registration later. Which doesn't follow the recommendation that a container should be considered immutable. Another alternativ would be to pass the container instance down to my WebHosts Startup class.
It seems that I need to have a registration of my container inside the container itself. How would I do that? Maybe there is a better approach? I hope it's clear what I am struggling with.
I am pretty sure there must be a better way to wire up webapi's resolver. Any ideas and feedback is very appreciated.
I solved it in the meantime, thanks to this post. We can inject an instance of ILifetimeScope to the constructor without having to register anything.
// Assembly A referencing B
public class Program
{
public static void Main(string[] args)
{
var builder = new ContainerBuilder();
buider.RegisterModule<MyBigModule>();
var container = builder.Build();
_ = HostFactory.Run(c =>
{
c.UseAutofacContainer(container);
c.Service<IMyServiceManager>(svc =>
{
svc.ConstructUsingAutofacContainer();
// ...
}
// ...
});
}
}
// Assembly B
public class WebHost : IWebHost
{
private readoly ILifetimeScope scope
public WebHost(ILifetimeScope scope)
{
this.scope = scope;
}
public void Start()
{
WebApp.Start("someUri", app => {
var config = new HttpConfiguration
{
DependencyResolver = new AutofacWebApiDependencyResolver(this.scope)
};
// ...
});
}
}
// Assembly B
public class MyBigModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<WebHost>.As<IWebHost>();
// ...
}
}

Controller and Action from Global Error Handler

I'm migrating an web api from .Net to .NetCore.
We had a custom ExceptionFilterAttribute to handle errors in a centralized way. Something like this:
public class HandleCustomErrorAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext filterContext)
{
// Error handling routine
}
}
With some search, I managed to create something similar on .Net Core
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
//logger.LogError($"Something went wrong: {contextFeature.Error}");
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error."
}.ToString());
}
});
});
}
}
I need to find a way to access these 3 info that where avaiable in .Net in .Net Core version:
filterContext.ActionContext.ActionDescriptor.ActionName;
filterContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName;
HttpContext.Current.Request.Url.ToString();
Is it possible ?
For a complete solution with registering ExceptionFilter and get request path, you could try like
ExceptinoFilter
public class ExceptinoFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
string controllerName = context.RouteData.Values["controller"].ToString();
string actionName = context.RouteData.Values["action"].ToString();
var request = context.HttpContext.Request;
var requestUrl = request.Scheme + "://" + request.Host + request.Path;
}
}
Register
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options => {
options.Filters.Add(new ExceptinoFilter());
});
}

XUnit DI through overridden Startup file (.net core)

I have build a WebAPI and apart from my tests running on Postman I would like to implement some Integration/Unit tests.
Now my business logic is very thin, most of the time its more of CRUD actions, therefore I wanted to start with testing my Controllers.
I have a basic setup. Repository pattern (interfaces), Services (business logic) and Controllers.
The flow goes Controller (DI Service) -> Service (DI Repo) -> Repo Action!
So what I did was override my Startup file to change into a in memory database and the rest should be fine (I would assume) Services are added, repos are added and now I am pointing into a in memory DB which is fine for my basic testing.
namespace API.UnitTests
{
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
: base(env)
{
}
public void ConfigureTestServices(IServiceCollection services)
{
base.ConfigureServices(services);
//services.Replace<IService, IMockedService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
base.Configure(app, env, loggerFactory);
}
public override void SetUpDataBase(IServiceCollection services)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services
.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
}
}
I wrote my first test, but the DatasourceService is not there:
The following constructor parameters did not have matching fixture data: DatasourceService datasourceService
namespace API.UnitTests
{
public class DatasourceControllerTest
{
private readonly DatasourceService _datasourceService;
public DatasourceControllerTest(DatasourceService datasourceService)
{
_datasourceService = datasourceService;
}
[Xunit.Theory,
InlineData(1)]
public void GetAll(int companyFk) {
Assert.NotEmpty(_datasourceService.GetAll(companyFk));
}
}
}
What am I missing?
You can't use dependency injection on test classes. You can only let xunit inject special fixtures via constructor (see docs).
For Integration Testing you want to use the TestServer class from Microsoft.AspNetCore.TestHost package and a separate Startup.cs class (easier to setup configuration than inheritance imho).
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureTestServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
services.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
public void Configure(IApplicationBuilder app)
{
// your usual registrations there
}
}
In your unit test project, you need to create an instance of the TestServer and perform the test.
public class DatasourceControllerTest
{
private readonly TestServer _server;
private readonly HttpClient _client;
public DatasourceControllerTest()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.GetAsync($"/api/datasource/{companyFk}");
// expected result from rest service
var expected = #"[{""data"":""value1"", ""data2"":""value2""}]";
// Assert
// This makes sure, you return a success http code back in case of 4xx status codes
// or exceptions (5xx codes) it throws an exception
response.EnsureSuccessStatusCode();
var resultString = await response.Content.ReadAsStringAsync();
Assert.Equals(resultString, expectedString);
}
}
Now, when you call operations which write to the database, you can also check if the data is really written to the database:
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
// expected result from rest service
// Assert
response.EnsureSuccessStatusCode();
// now check if its really gone in the database. For this you need an instance
// of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
// and it has a property Services which is the IoC container
var provider = _server.Host.Services;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();
// if it was deleted, the query should result in false
Assert.False(result);
}
Now you can use Xunit.DependencyInjection in your tests.
namespace Your.Test.Project
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDependency, DependencyClass>();
}
}
}
your DI-classes:
public interface IDependency
{
int Value { get; }
}
internal class DependencyClass : IDependency
{
public int Value => 1;
}
and XUnit-test:
public class MyAwesomeTests
{
private readonly IDependency _d;
public MyAwesomeTests(IDependency d) => _d = d;
[Fact]
public void AssertThatWeDoStuff()
{
Assert.Equal(1, _d.Value);
}
}