I have an issue where I can't seem to send new data to the connected Signal R clients from a ChangedEventHandler. The docs says that I can get the hub context by using:-
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.addToList(insertedCustomer);
However nothing gets sent to the clients (checked on fiddler) or any errors reported. My onchange event is wired up at the moment from Application_Start as I am creating a proof of concept. I should point out the hub does work on start up and retrieves the data from the initial GetAll call
protected void Application_Start()
{
...
_sqlTableDependency.OnChanged += _sqlTableDependency_OnChanged;
_sqlTableDependency.Start();
...
}
private void _sqlTableDependency_OnChanged(object sender, RecordChangedEventArgs<BiddingText> e)
{
switch (e.ChangeType)
{
case ChangeType.Insert:
foreach (var insertedCustomer in e.ChangedEntities)
{
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.addToList(insertedCustomer);
biddingTextList.Add(insertedCustomer);
}
break;
}
}
When I put a breakpoint on the hub context I get my ChatHub back.
My Javascript code:
$.connection.hub.url = "http://localhost:37185/signalr";
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
chat.client.initialText = function(data) {
var index;
//console.log(data.length);
for (index = 0; index < data.List.length; ++index) {
$('#list').append("<li>" + data.List[index].text + "</li>");
}
};
chat.client.addToList = function(data) {
console.log(data);
$('#list').append("<li>" + data.text + "</li>");
};
// Start the connection.
$.connection.hub.start({ jsonp: true }).done(function () {
chat.server.getAll(1831);
});
My Hub code:
public class ChatHub : Microsoft.AspNet.SignalR.Hub
{
private readonly IMediator mediator;
public ChatHub(IMediator mediator)
{
this.mediator = mediator;
}
public void GetAll(int saleId)
{
var model = mediator.Request(new BiddingTextQuery { SaleId = saleId});
Clients.Caller.initialText(model);
}
}
Not sure if this is relevant but the Clients.Connection.Identity is different everytime I use GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
Can anyone help?
I had some similar issues a while back setting up a Nancy API to publish some events to SignalR clients.
The core issue I had was that I had failed to ensure Nancy and SignalR was using the same DI container at the SignalR global level.
SignalR, as Nancy, has a default DependencyResolver that is used to resolve any dependencies in your hubs. When I failed to implement the same source of dependencies for Nancy and SignalR I basically ended up with two separate applications.
Small disclaimer: You have not posted your config code, so my solution here is based on some assumptions (as well as the following twitter answer from David Fowler when you reached out on twitter:
#rippo you have a custom dependency resolver and global has has another. You need to use one container
(https://twitter.com/davidfowl/status/635000470340153344)
Now some code:
First you need to implement a custom SignalR depependency resolver, and ensure it uses the same source of dependencies as the rest of your application.
This is the implementation I used for an Autofac container:
using Autofac;
using Autofac.Builder;
using Autofac.Core;
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LabCommunicator.Server.Configuration
{
internal class AutofacSignalrDependencyResolver : DefaultDependencyResolver, IRegistrationSource
{
private ILifetimeScope LifetimeScope { get; set; }
public AutofacSignalrDependencyResolver(ILifetimeScope lifetimeScope)
{
LifetimeScope = lifetimeScope;
var currentRegistrationSource = LifetimeScope.ComponentRegistry.Sources.FirstOrDefault(s => s.GetType() == GetType());
if (currentRegistrationSource != null)
{
((AutofacSignalrDependencyResolver)currentRegistrationSource).LifetimeScope = lifetimeScope;
}
else
{
LifetimeScope.ComponentRegistry.AddRegistrationSource(this);
}
}
public override object GetService(Type serviceType)
{
object result;
if (LifetimeScope == null)
{
return base.GetService(serviceType);
}
if (LifetimeScope.TryResolve(serviceType, out result))
{
return result;
}
return null;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
object result;
if (LifetimeScope == null)
{
return base.GetServices(serviceType);
}
if (LifetimeScope.TryResolve(typeof(IEnumerable<>).MakeGenericType(serviceType), out result))
{
return (IEnumerable<object>)result;
}
return Enumerable.Empty<object>();
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var typedService = service as TypedService;
if (typedService != null)
{
var instances = base.GetServices(typedService.ServiceType);
if (instances != null)
{
return instances
.Select(i => RegistrationBuilder.ForDelegate(i.GetType(), (c, p) => i).As(typedService.ServiceType)
.InstancePerLifetimeScope()
.PreserveExistingDefaults()
.CreateRegistration());
}
}
return Enumerable.Empty<IComponentRegistration>();
}
bool IRegistrationSource.IsAdapterForIndividualComponents
{
get { return false; }
}
}
}
Next, in your SignalR config, you need to assign the custom dependency resolver:
Note: My app was using owin, so you may not need the HubConfiguration bit, but you need the GlobalHost bit (which is the one I messed up when my stuff was not working).
var resolver = new AutofacSignalrDependencyResolver(container);
'Owin config options.
var config = new HubConfiguration()
{
Resolver = resolver,
EnableDetailedErrors = true,
EnableCrossDomain = true
};
GlobalHost.DependencyResolver = resolver;
'More owin stuff
app.MapHubs(config);
Hope this will help resolve your issue.
You need to keep track of the clients that connect to the hub, and then send them the new messages, something like this
This is a base class I wrote for my Hubs
/// <summary>
/// base class for Hubs in the system.
/// </summary>
public class HubBase : Hub {
/// <summary>
/// The hub users
/// </summary>
protected static ConcurrentDictionary<Guid, HubUser> Users = new ConcurrentDictionary<Guid, HubUser>();
/// <summary>
/// Called when the connection connects to this hub instance.
/// </summary>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task" />
/// </returns>
public override System.Threading.Tasks.Task OnConnected() {
Guid userName = RetrieveUserId();
string connectionId = Context.ConnectionId;
HubUser user = Users.GetOrAdd(userName, _ => new HubUser {
UserId = userName,
ConnectionIds = new HashSet<string>()
});
lock (user.ConnectionIds) {
user.ConnectionIds.Add(connectionId);
}
return base.OnConnected();
}
/// <summary>
/// Called when a connection disconnects from this hub gracefully or due to a timeout.
/// </summary>
/// <param name="stopCalled">true, if stop was called on the client closing the connection gracefully;
/// false, if the connection has been lost for longer than the
/// <see cref="P:Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout" />.
/// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout.</param>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task" />
/// </returns>
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled) {
try {
Guid userName = RetrieveUserId();
string connectionId = Context.ConnectionId;
HubUser user;
Users.TryGetValue(userName, out user);
if (user != null) {
lock (user.ConnectionIds) {
user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId));
if (!user.ConnectionIds.Any()) {
HubUser removedUser;
Users.TryRemove(userName, out removedUser);
}
}
}
} catch {
//Bug in SignalR causing Context.User.Identity.Name to sometime be null
//when user disconnects, thus remove the connection manually.
lock (Users) {
HubUser entry = Users.Values.FirstOrDefault(v => v.ConnectionIds.Contains(Context.ConnectionId));
if (entry != null) entry.ConnectionIds.Remove(Context.ConnectionId);
}
}
return base.OnDisconnected(stopCalled);
}
private Guid RetrieveUserId() {
Cookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket decryptedCookie = FormsAuthentication.Decrypt(authCookie.Value);
var user = JsonConvert.DeserializeObject<User>(decryptedCookie.UserData);
return user.Id;
}
}
Then the Hub code is
/// <summary>
/// A hub for sending alerts to users.
/// </summary>
public class AlertHub : HubBase, IAlertHub {
/// <summary>
/// Sends the alert.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="userId">The user identifier.</param>
public void SendAlert(string message, Guid userId) {
HubUser user;
Users.TryGetValue(userId, out user);
if (user != null) {
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AlertHub>();
context.Clients.Clients(user.ConnectionIds.ToList()).sendAlert(message);
}
}
/// <summary>
/// Send alert to user.
/// </summary>
/// <param name="returnId">The return identifier.</param>
/// <param name="userId">The user identifier.</param>
public void ReturnProcessedAlert(Guid returnId, Guid userId) {
HubUser user;
Users.TryGetValue(userId, out user);
if (user != null) {
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<AlertHub>();
context.Clients.Clients(user.ConnectionIds.ToList()).returnProcessedAlert(returnId);
}
}
}
Related
I have created a web api project and run as a windows service.
I have added MEF to load a dll (LibOne.dll) and use it in the controller (ValueController). But unable to get the values in the imported interface.
I followed this link to implement this and added MefDependencyResolver in my web api project.
How to integrate MEF with ASP.NET MVC 4 and ASP.NET Web API
in the values controller the IMyClass is returning always null. How to resolve this?
Is there anything I am mising?
Here is my Web Api Controller
[Export(typeof(ValuesController))]
public class ValuesController : ApiController
{
[Import(typeof(IMyClass))]
private IMyClass Myclass { get; set; }
public String GetString(Int32 id)
{
return Myclass.GetValues();
}
}
SelfHosted Service to run the web api as windows service
protected override void OnStart(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
Thread.Sleep(10000);
MefConfig.RegisterMef(config);
config.Routes.MapHttpRoute(
name: "API",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
}
MEF registration
public static void RegisterMef(HttpConfiguration config)
{
var assemblyCatalog = new AggregateCatalog();
assemblyCatalog.Catalogs.Add(new DirectoryCatalog(#""));
var container = new CompositionContainer(assemblyCatalog);
var resolver = new MefDependencyResolver(container);
config.DependencyResolver = resolver;
}
Exporting this class from LibOne.dll
[Export(typeof(IMyClass))]
public class Super :IMyClass
{
public string GetValues()
{
return "My value";
}
}
MEFDependencyResolver:
internal class MefDependencyResolver : IDependencyResolver
{
private readonly CompositionContainer _container;
/// <summary />
/// <param name="container"></param>
public MefDependencyResolver(CompositionContainer container)
{
_container = container;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public IDependencyScope BeginScope()
{
return this;
}
/// <summary>
/// Called to request a service implementation.
///
/// Here we call upon MEF to instantiate implementations of dependencies.
/// </summary>
/// <param name="serviceType">Type of service requested.</param>
/// <returns>Service implementation or null.</returns>
public object GetService(Type serviceType)
{
if (serviceType == null)
throw new ArgumentNullException("serviceType");
var name = AttributedModelServices.GetContractName(serviceType);
var export = _container.GetExportedValueOrDefault<object>(name);
return export;
}
/// <summary>
/// Called to request service implementations.
///
/// Here we call upon MEF to instantiate implementations of dependencies.
/// </summary>
/// <param name="serviceType">Type of service requested.</param>
/// <returns>Service implementations.</returns>
public IEnumerable<object> GetServices(Type serviceType)
{
if (serviceType == null)
throw new ArgumentNullException("serviceType");
var exports = _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
return exports;
}
/// <summary>
///
/// </summary>
public void Dispose()
{
}
}
Currently my code looks something like this:
public class UserController : ControllerBase
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if (someValidLogic){
return Ok(new { message = "User login success.",
additionalParameters = new {
param1 = "",
param2 = ""
}
});
}
else {
return BadRequest(new { errorMessage = "Username or password is incorrect.",
additionalParameters = {
StatusCode = 400,
RetryLeftCount = 3 }
});
}
}
}
I am manually creating JSON objects to return to UI in every endpoint so that I can have consistent communication and allow UI to handle messages at a global level. (using angular interceptors in my case). I am wanting to create a custom class that can be implemented by all controllers that have only two options of return types - Success(), Fail().
public class UserController : CustomControllerBase
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if (someValidLogic){
return Success("User login success.", additionalParameters)
}
else {
return Error("Username or password is incorrect.", additionalParameters);
}
}
}
And my CustomControllerBase would be in charge of formatting the results in the proper form. I know I can do this via middleware but I really do not want to as it still allows developers to accidentally send back some non-valid results and middleware not knowing how to handle it.
Even if you make a custom base controller you're going to have to make the custom base controller extend Microsoft.AspNetCore.Mvc.ControllerBase, else you won't get all the automatic routing, the [HttpPost] type attributes etc. And once you extend ControllerBase, all the methods like Ok() will be available to your other developers. I'd really just try to communicate with the developers to not use Ok() etc.
This is hacky, but you could "block" the other methods by overriding them in your CustomControllerBase and just throwing an exception. This won't create a compile time error but it will at least create a shallow runtime exception. Also there's a LOT of these methods and you'd have to override them all.
public class CustomControllerBase : ControllerBase
{
public ActionResult Success(string message)
{
return base.Ok(message);
}
public new ActionResult Ok(object value)
{
throw new Exception("don't use this");
}
}
public class UserController : CustomControllerBase
{
public async Task<ActionResult> Hello()
{
return Ok("hello"); // throws runtime exception
}
}
Alternatively use a style checker and outlaw all uses of Ok() and similar methods. You'd also have to disallow lines like return new OkObjectResult("hello"); It's really going to be quite an ordeal to get this right.
As you've requested, to have a custom class that you can re-use in returning response messages back to the calling request, I created a custom class that inherits from ActionResult that we can return.
After having this class in place, we are going to use it to create a base/custom controller where we will add our Success and Fail methods which any controller can inherit from to use the extended methods.
CustomActionResult
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text
/// <summary>
/// Customized <see cref="ActionResult"/> that allows easily setting <see cref="HttpStatusCode"/>
/// and data result object to be returned for a request.
/// </summary>
public class CustomActionResult : ActionResult
{
private static UTF8Encoding utf = new UTF8Encoding();
/// <summary>
/// Http response code.
/// </summary>
public HttpStatusCode StatusCode { get; set; }
/// <summary>
/// Data to return back to the user as an <see cref="object"/> of any <see cref="Type"/>
/// </summary>
public object Data { get; set; }
/// <summary>
/// Parameterless contructor that initializes the ActionResult with
/// <see cref="HttpStatusCode.OK"/> as the default Response Code.
/// </summary>
public CustomActionResult()
{
StatusCode = HttpStatusCode.OK;
Headers = new Dictionary<string, string>();
}
/// <summary>
/// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
/// </summary>
/// <param name="statusCode">
/// Http response code to set for this ActionResult.
/// </param>
public CustomActionResult(HttpStatusCode statusCode)
:this()
{
StatusCode = statusCode;
}
/// <summary>
/// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
/// </summary>
/// <param name="statusCode">
/// Http response code to set for this ActionResult.
/// </param>
/// <param name="message">Reason phrase</param>
public CustomActionResult(HttpStatusCode statusCode, string message)
:this()
{
StatusCode = statusCode;
Data = message;
}
private string Json
{
get
{
if(Data != null)
{
if (Data.GetType() == typeof(string))
{
return Data.ToString();
}
return JsonConvert.SerializeObject(Data);
}
return string.Empty;
}
}
public byte[] GetBuffer() => utf.GetBytes(Json);
public Dictionary<string,string> Headers { get; private set; }
public override void ExecuteResult(ActionContext context)
{
if (Headers.Count > 0)
{
for (int i = 0; i < Headers.Count; i++)
{
var item = Headers.ElementAt(i);
context.HttpContext.Response.Headers.Add(item.Key, item.Value);
}
}
if (!string.IsNullOrWhiteSpace(Json))
{
context.HttpContext.Response.ContentType = "application/json";
}
context.HttpContext.Response.StatusCode = (int)StatusCode;
context.HttpContext.Response.Body.Write(GetBuffer(), 0, GetBuffer().Length);
}
}
CustomBaseController
public class CustomBaseController : ControllerBase
{
public ActionResult Success(object value)
{
return new CustomActionResult(){ Data = value };
}
public ActionResult Success(string message, params object[] additionalParams)
{
if(additionalParams.Length > 0){
return new CustomActionResult(){
Data = new { message, additionalParams }
};
}else{
return new CustomActionResult() { Data = message };
}
}
public ActionResult Fail(object value)
{
return new CustomActionResult(HttpStatusCode.BadRequest){ Data = value };
}
public ActionResult Fail(string message, params object[] additionalParams)
{
if(additionalParams.Length > 0){
return new CustomActionResult(HttpStatusCode.BadRequest){
Data = new { ErrorMessage = message, additionalParams }
};
}else{
return new CustomActionResult(HttpStatusCode.BadRequest){
Data = new { ErrorMessage = message }
};
}
}
}
Usage
public class UserController : CustomBaseController
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if(!ModelState.IsValid)
return this.Fail(ModelState);
// add your other custom logic here
if(someLogic){
return this.Success("your-message", additionalParams);
} else {
return this.Fail("custom-error-message", additionalParams);
}
}
}
I am using SignalR library to show online Users. At present i am able to display total number of online users but when i am diplay the list of online user with their name it makes me confused.
I don't have any idea that how could i manage the particular online status at present i want to manage the particular online user status by database. I have table which i want to manage on Onconnected and OnDisconnected method.
Please give me an idea that how could i display list of online user
Here it is my code.
<script src="~/Content/Scripts/jquery-ui.min.js"></script>
<script src="~/Content/Scripts/jquery.ui.touch-punch.js"></script>
<script src="~/Content/Scripts/jquery.signalR-1.1.3.js"></script>
<script src="/signalr/hubs" type="text/javascript"></script>
<script>
$(document).ready(function () {
$(function () {
// Reference the auto-generated proxy for the hub.
var userActivity = $.connection.userActivityHub;
var Chat = $.connection.Chat;
// Create a function that the hub can call back to display messages.
userActivity.client.updateUsersOnlineCount = function (count) {
// Add the message to the page.
console.log('Count :' + count);
$('#usersCount').text(count);
};
//Chat.server.SetName($.connection.hub.id, $("#displayname").val())
$.connection.hub.start();
});
});
</script>
I have a HubClass named UserActivityHub.
using System.Collections.Generic;
using System;
using System.Web;
using IMWedding.BAL.UserInfos;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
namespace IMWedding.Utils
{
[HubName("userActivityHub")]
public class UserActivityHub : Hub
{
IUserInfosRepository _userRepo;
public UserActivityHub()
{
this._userRepo = new UserInfosRepository();
}
/// <summary>
/// The count of users connected.
/// </summary>
public static List<string> Users = new List<string>();
/// <summary>
/// Sends the update user count to the listening view.
/// </summary>
/// <param name="count">
/// The count.
/// </param>
public void Send(int count)
{
// Call the addNewMessageToPage method to update clients.
var context = GlobalHost.ConnectionManager.GetHubContext<UserActivityHub>();
context.Clients.All.updateUsersOnlineCount(count);
}
/// <summary>
/// The OnConnected event.
/// </summary>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
///
public override System.Threading.Tasks.Task OnConnected()
{
string clientId = GetClientId();
if (Users.IndexOf(clientId) == -1)
{
Users.Add(clientId);
}
//if (!string.IsNullOrEmpty(Convert.ToString(HttpContext.Current.Session["UserInfoID"])))
//{
// var detail = _userRepo.GetUserDetailByUserID(UserId);
// if (detail != null)
// {
// if (!string.IsNullOrEmpty(clientId))
// {
// detail.CreatedBy = UserId;
// bool Result = _userRepo.AddUserDetail(detail);
// }
// }
//}
// Send the current count of users
Send(Users.Count);
return base.OnConnected();
}
public void SetName(string cid, string name)
{
//Users.Find(uo => uo.con_id == cid).client_name = name;
}
/// <summary>
/// The OnReconnected event.
/// </summary>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
//public override System.Threading.Tasks.Task OnReconnected()
//{
// string clientId = GetClientId();
// if (Users.IndexOf(clientId) == -1)
// {
// Users.Add(clientId);
// }
// // Send the current count of users
// Send(Users.Count);
// return base.OnReconnected();
//}
/// <summary>
/// The OnDisconnected event.
/// </summary>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
public override System.Threading.Tasks.Task OnDisconnected(bool StopCalled)
{
string clientId = GetClientId();
if (Users.IndexOf(clientId) > -1)
{
Users.Remove(clientId);
}
if (!string.IsNullOrEmpty(clientId))
{
bool Result = _userRepo.RemoveDetail(clientId);
}
// Send the current count of users
Send(Users.Count);
return base.OnDisconnected(StopCalled);
}
/// <summary>
/// Get's the currently connected Id of the client.
/// This is unique for each client and is used to identify
/// a connection.
/// </summary>
/// <returns>The client Id.</returns>
private string GetClientId()
{
string clientId = "";
if (Context.QueryString["clientId"] != null)
{
// clientId passed from application
clientId = this.Context.QueryString["clientId"];
}
if (string.IsNullOrEmpty(clientId.Trim()))
{
clientId = Context.ConnectionId;
}
return clientId;
}
}
}
The application_start method in global.asax File
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
MailSchedulerModel objmodel = new MailSchedulerModel();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// GlobalFilters.Filters.Add(new )
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Here it is my Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.MapSignalR();
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
Firstly, create SendUserList method inside UserActivityHub hub.
public void SendUserList(List<string> users)
{
var context = GlobalHost.ConnectionManager.GetHubContext<UserActivityHub>();
context.Clients.All.updateUserList(users);
}
Then, inside System.Threading.Tasks.Task OnConnected() method call SendUserList method;
public override System.Threading.Tasks.Task OnConnected()
{
string clientId = GetClientId();
if (Users.IndexOf(clientId) == -1)
{
Users.Add(clientId);
}
//if (!string.IsNullOrEmpty(Convert.ToString(HttpContext.Current.Session["UserInfoID"])))
//{
// var detail = _userRepo.GetUserDetailByUserID(UserId);
// if (detail != null)
// {
// if (!string.IsNullOrEmpty(clientId))
// {
// detail.CreatedBy = UserId;
// bool Result = _userRepo.AddUserDetail(detail);
// }
// }
//}
// Send the current users
SendUserList(Users);
return base.OnConnected();
}
Lastly, inside javascript part insert updateUserList function to handle userList which is pushed from server;
$(document).ready(function () {
$(function () {
// Reference the auto-generated proxy for the hub.
var userActivity = $.connection.userActivityHub;
var Chat = $.connection.Chat;
// Create a function that the hub can call back to display messages.
userActivity.client.updateUsersOnlineCount = function (count) {
// Add the message to the page.
console.log('Count :' + count);
$('#usersCount').text(count);
};
userActivity.client.updateUserList = function (userList) {
//Take action with userList
};
//Chat.server.SetName($.connection.hub.id, $("#displayname").val())
$.connection.hub.start();
});
});
Yesterday I coded myself a simple RESTful web API in .NET Core (solution named Vault) with a single method that gets the profile of the user depending only on the Windows user name. I now have a second solution that will call some requests to my self-hosting service previously mentioned. When I use Postman, I can retrieve the data with ease when I call a GET on my only method in Vault, but when I build the URI in Mainframe and execute, I get an Unauthorized error and it confuses me as Vault does not require specific login like username and password. I also put a breakpoint in Vault and unlike when I'm using Postman, it does not reach my code when calling via the Mainframe solution.
Here where is build my REST request and call the service (GetProfile())
public VaultApiClient(ConfigurationManagerWrap configuration)
{
this.configuration = configuration;
this.client = new RestClient(new Uri(this.configuration.GetAppSetting<string>(ConfigurationKeys.VaultApiURL)));
}
/// <summary>
/// The get profile.
/// </summary>
/// <returns>
/// The <see cref="UserProfile"/>.
/// </returns>
public UserProfile GetProfile()
{
var request = new RestRequest("profile") { Method = Method.GET};
//request.AddParameter("profile", ParameterType.UrlSegment);
var response = this.client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception(
$"Could not get the user profile ({response.StatusCode} {response.StatusDescription})");
}
return RestJsonSerializer.Default.Deserialize<UserProfile>(response);
}
Im hosting local so the base URI, aka ConfigurationKeys.VaultApiURL, is localhost5000/api/
My Mainframe controller :
public HomeController()
: this(new VaultApiClient(new ConfigurationManagerWrap()))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HomeController"/> class.
/// </summary>
/// <param name="vaultApiClient">
/// The vault api client.
/// </param>
public HomeController(IVaultApiClient vaultApiClient)
{
this.vaultApiClient = vaultApiClient;
}
/// <summary>
/// The index.
/// </summary>
/// <returns>
/// The <see cref="ActionResult"/>.
/// </returns>
public ActionResult Index()
{
var profile = this.GetProfile();
this.ViewBag.IsEdit = false;
this.ViewBag.IsError = false;
this.ViewBag.ErrorMessage = "";
if (this.TempData.ContainsKey("IsEdit"))
{
this.ViewBag.IsEdit = true;
this.TempData.Remove("IsEdit");
if (this.TempData.ContainsKey("ErrorMessage"))
{
this.ViewBag.IsError = true;
this.ViewBag.ErrorMessage = this.TempData["ErrorMessage"];
this.TempData.Remove("ErrorMessage");
}
}
return this.View("Index", profile);
}
private UserProfile GetProfile()
{
return this.vaultApiClient.GetProfile();
}
And here is the vault controller method that handles the GET request in question:
[HttpGet]
[Route("/api/Profile")]
[Produces(typeof(UserProfile))]
public IActionResult SearchProfile()
{
try
{
if (!this.currentuser.IsAuthenticated)
{
throw new Exception("This service does not support anonymous calls.");
}
var profile = Task.Run(() => this.personalizationService.GetUserProfileAsync(this.currentuser.GetCurrentWindowsIdentityName)).Result;
var userProfile = this.persistenceToDataModelConverter.Convert(profile);
userProfile.UserAdLogin = this.currentuser.GetCurrentWindowsIdentityName;
return this.Ok(userProfile);
}
catch (Exception ex)
{
return this.NotFound(ex);
}
}
Lastly, here are a few pics of before and when the error is thrown.
Credential information must be provided with client request in order to authenticate with the server.
I am trying to make a custom filter for my web api controllers part of a ASP.NET MVC 5 website/application to check the requests headers for a specific token which i have stored in the database. Most examples I found were containing user credentials and then the user was authenticated using identity. Not exactly what I am looking for.
This is the tutorial I found and am currently following.
The web API should only handle "external" HTTP calls, the website side will have its own controllers presently (but may be subject to change).
This filter should interface with identity 2 system already present if possible.
What I do is that I send user credentials then, assign a token to the user and then I want to use that token to authenticate the request. Is there a way I can just filter the request based on the token or do I need to use Owin identity and their token management. I am using a mobile client (currently iOS, will include android too) to make the calls. Any example or tutorial I could refer to ?
The token is currently a random combination of alphanumeric characters and symbols.
Thank you.
P.S. I can post code snippets and stuff where needed.
Edit: The HTTPRequests will be filtered based on whether they contain a token present within our database/system. Requests that do contain a token or are not present within our system will receive unauthorised error (401?)
Suppose you thought that sending user name and password to every request is not good.Refer my below implementation with out user name and password, since we are don't send user name and password with every request.
public class AuthenticationFilter : AuthorizationFilterAttribute
{
/// <summary>
/// read requested header and validated
/// </summary>
/// <param name="actionContext"></param>
public override void OnAuthorization(HttpActionContext actionContext)
{
var identity = FetchFromHeader(actionContext);
if(identity != null)
{
var securityService = actionContext.ControllerContext.Configuration.DependencyResolver.GetService(typeof(ILoginService)) as ILoginService;
if (securityService.TokenAuthentication(identity))
{
CurrentThread.SetPrincipal(new GenericPrincipal(new GenericIdentity(identity), null), null, null);
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
return;
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
return;
}
base.OnAuthorization(actionContext);
}
/// <summary>
/// retrive header detail from the request
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
private string FetchFromHeader(HttpActionContext actionContext)
{
string requestToken = null;
var authRequest = actionContext.Request.Headers.Authorization;
if (authRequest != null && !string.IsNullOrEmpty(authRequest.Scheme) && authRequest.Scheme == "Basic")
requestToken = authRequest.Parameter;
return requestToken;
}
}
You can make this filter unit testable by injecting the service dependencies via property(property injection). For custom attributes, we don't want to pass the dependencies via constructor. We want the attribute easy to
use. To rewrite what #Raj already started, it can look like this:
public class AuthenticationFilter : AuthorizationFilterAttribute
{
[Dependency]
public ILoginService LoginService { get; set; }
/// <summary>
/// read requested header and validated
/// </summary>
/// <param name="actionContext"></param>
public override void OnAuthorization(HttpActionContext actionContext)
{
var identity = FetchFromHeader(actionContext);
if (identity != null)
{
if (LoginService.TokenAuthentication(identity))
{
CurrentThread.SetPrincipal(new GenericPrincipal(new GenericIdentity(identity), null), null, null);
//IPrincipal principal = new GenericPrincipal(new GenericIdentity(identity), new string[] { "myRole" });
//Thread.CurrentPrincipal = principal;
//HttpContext.Current.User = principal;
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
return;
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
return;
}
base.OnAuthorization(actionContext);
}
/// <summary>
/// retrive header detail from the request
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
private string FetchFromHeader(HttpActionContext actionContext)
{
string requestToken = null;
var authRequest = actionContext.Request.Headers.Authorization;
if (authRequest != null && !string.IsNullOrEmpty(authRequest.Scheme) && authRequest.Scheme == "Basic")
requestToken = authRequest.Parameter;
return requestToken;
}
}