In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens
I'm using ASP.NET Core 2.1 RC1.
I'm also using Signal-R for it (found here):
https://learn.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-2.1
I'm creating a .NET Core console application that's hosting Kestrel and using Signal-R. I've pretty much set it up exactly as the getting started documentation states for setting up the Startup.
This all works great. I'm able to connect to the it, get my HTML with signal-R script in it, receive messages I crafted with Clients.All.SendAsync. Works great.
BUT
I want to be able to send a message to clients, from outside the Hub. Where some event happens in my application, and a message is sent to clients. In full .NET, I'd use the GlobalHost and get the context. In ALL my searches on Stack Overflow, they reference something that no longer works, or used within an REST API controller that's passed in the IHubContext.
I have an event listener in my program.cs, and when the event is triggered, I'd love to be able to send a message to my UserInterfaceHub.
So -- how do I get the hub context in Program.CS - so I can send messages to it (call the SwitchUI method) from within an event delegate I have in Program.CS?
StartUp.cs
public void ConfigureServices(IServiceCollection services) {
services.Configure<CookiePolicyOptions>(options => {
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc();
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder => {builder.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin().AllowCredentials();}));
services.AddSignalR();
var provider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseSignalR(routes => {routes.MapHub<UserInterfaceHub>("/uihub");});
app.UseMvc();
//app.Run(async (context) =>{await context.Response.WriteAsync("Active");});
}
Program.CS
CreateWebHostBuilder(args)
.UseKestrel()
.UseUrls("http://0.0.0.0:" + appProperties.HostPort.ToString().Trim())
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Build()
.Start();
UserInterfaceHub.cs
namespace InterfaceModule.Hubs {
public class UserInterfaceHub : Hub {
public async Task SwitchUI(string message) {
await Clients.All.SendAsync("ReceiveEvent", message);
}
public override async Task OnConnectedAsync() {
//await SwitchUI("HOWDY NEW PERSON!");
await base.OnConnectedAsync();
}
}
}
edit adding clarity.
In Program.CS, I have this event delegate:
//now that we're ready, start listening.
await
deviceClient.SetInputMessageHandlerAsync(ModuleProperties.InputName, OnReceiveEvent, deviceClient);
Console.WriteLine("INIT: Event Message Input handler created: [{0}]", ModuleProperties.InputName);
which is this:
static async Task<MessageResponse> OnReceiveEvent(Message message, object userContext) {
//HOW DO I REACH THE HUB FROM HERE SO I CAN SEND A MESSAGE TO THE LISTENERS?
}
I encountered a similar situation and here's how to resolve it:
In your Service layer, create an interface called something like ISendHubMessage. Have a method called Send() that takes parameters that you're wanting to send via SignalR. Create a class within the same file called SendHubMessage that implements the interface. Have it just do a return.
In your top-level project (where your Startup.cs file is located) create another class called SendHubMessage that implements that same ISendHubMessage interface from your Service layer. Within this SendHubMessage, you can use DI to get at the hub as explained above. This method will do the actual logic of sending via SignalR.
In your Startup ConfigureServices() method, add the following line:
services.AddTransient<"Service".ISendHubMessage, "TopLevel".SendHubMessage>();
(where "Service" is the namespace to your Service-level project and "TopLevel" in the namespace to your top-level project).
What you're doing with this line is saying "Whenever an object requests the ISendHubMessage dependency from the Service layer, supply it with the SendHubMessage class defined in my top-level project".
Finally, in all the places in code outside of your top-level project that you're wanting to send messages through your hub, inject that ISendHubMessage dependency in the constructor. You can then refer to it in the class methods and when you call Send(), it will call the Send() method defined in your SendHubMessage class in your top-level project.
This line of code:
app.UseSignalR(routes => {routes.MapHub<UserInterfaceHub>("/uihub");});
will register your hub with the DI container. Then to get access to it, you either use constructor injection to inject in the IHubContext<UserInterfaceHub> (this works for example in a Web Controller) or access it directly from the DI container by doing the following:
var hub = app.ApplicationServices.GetRequiredService<IHubContext<UserInterfaceHub>>();
(for example if executed in the startup.cs Configure method)
If you don't have access to the app.ApplicationServices which is basically an IServiceProvider at the location you need to access the hub, then you will need to either 1) get that class to work with dependency injection to inject in the IHubContext<UserInterfaceHub> or IServiceProvider 2) Setup a static Services global var via Configure so that you can have access to one of them globally, or find some other way to access the DI container (aka IServiceProvider) to get your hub via the above line of code.
Once you have your hub, then sending the message to the registered clients is a simple as calling the method on your hub.
await hub.Clients.All.SendAsync("ReceiveEvent", message);
You're question is a little unclear, but I'm assuming you mean you want to replace the following with something that can send a message through your hub:
app.Run(async (context) =>{await context.Response.WriteAsync("Active");});
Since this is in your Configure method, you can simply add IServiceCollection services to your Configure methods params. Then, you can do:
var hub = services.GetRequiredService<IHubContext<MyHub>>();
However, I'm not sure that this will actually do anything useful ultimately. At startup, you'd logically have no clients with subscriptions yet. As a result, sending a message through your hub at this point, would essentially go nowhere. By the time a user actually hits your site and gets connected to your hub, this part of your application has already run, and won't be hit again.
I have a WCF service that is defined in a module. When we try to call this service from a non-default tenant, the content manager always references our default tenants settings. In debugging, inside of OrchardServiceHostFactory,
I notice that it ends up getting the settings for the default tenant because the base address that is passed into the CreateServiceHost method is always our default tenants uri.
Given that I am not wholly familiar with WCF, is there a configuration option that I am missing that is causing the WCF service to be created with the default tenants address, instead of the non-default tenant?
Relevant code:
private static readonly Route _SITEMAP_SERVICE_ROUTE = new ServiceRoute("api/SitemapService", new OrchardServiceHostFactory(), typeof(ISitemapService))
{
DataTokens = new RouteValueDictionary
{
{
"area", "Project.Localization"
}
}
};
public interface ISitemapService : IOrchardSitemapService, IDependency
{
}
[ServiceContract]
public interface IOrchardSitemapService
{
[OperationContract]
int GetNavigableContentCount();
[OperationContract]
List<SitemapEntry> GetNavigableContent();
}
I was able to fix this by adding an additional site to IIS that pointed to the same file system location, and used the same application pool. This new site then references the non-default's tenant, and the service will now be created with the correct base address.
I am trying to add RequireHttpsAttribute attribute to MVC filters collection to push web site to HTTPS when it is deployed on prod server. The problem is with HttpContext.Current.Request.IsLocal line, the Request object is not available yet. Then how to check is site running localy or on prod server in RegisterGlobalFilters?
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (!HttpContext.Current.Request.IsLocal) //Exception here!!!
{
filters.Add(new RequireHttpsAttribute());
}
}
In this method you are to register the filters that will do the checking when the request comes in. This method will only get called once each time the application is started. So here you need to do something along the lines of:
filters.Add(new MyAuthorizeAttribute());
With MyAuthorizeAttribute being something along the lines of:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
If(!httpContext.Request.IsLocal)
{
**//Check for HTTPS and return false if need be**
}
}
Of course it does not need to be an AuthorizeAttribute.
EDIT
As I said before this method is called only once at the start of the application so there is no request for you to check in here. Here you can only apply filters that will be called every time a request is received. It is inside those filters that you can check request specific properties.
If you insist on using the RequireHttpsAttribute, than you either have to apply it to all methods regardless of whether the request is local or not or you have to extend RequireHttpsAttribute and override HandleNonHttpsRequest to handle local requests.
Is there a way to configure a rest web service to allow only one connection at a time?
I am using Wildfly 9.0.1-Final with the resteasy 3.0.11.Final implementation.
You can use synchronized block on static field:
private static final Object LOCK = new Object();
#GET
#Path("find")
#Produces(MediaType.APPLICATION_JSON)
public Response find(){
synchronized(LOCK){
//your code
}
}