I have the following code for a MQTT Subscriber in a Background Task:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using PickByLight.BackgroundTask.Models;
using PickByLight.Database.Wrapper.Interfaces;
using PickByLight.Logic;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
namespace PickByLight.BackgroundTask
{
/// <summary>
/// Hosted MQTT Background Service
/// </summary>
public class HostedMQTTService : IHostedService, IDisposable
{
private readonly Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
/// <summary>
/// MQTT Client
/// </summary>
private MqttClient MqttClient { get; set; }
/// <summary>
/// Name of the Pick by Light
/// </summary>
private string PickByLight_Name { get; set; }
/// <summary>
/// MQTT is activated
/// </summary>
private bool MqttIsActive { get; set; }
/// <summary>
/// IP Adress of the MQTT URL
/// </summary>
private string MqttURL { get; set; }
/// <summary>
/// Storage Process for an material
/// </summary>
private MaterialStorageProcess StorageProcess { get; set; }
/// <summary>
/// Service Scope Factory
/// </summary>
private IServiceScopeFactory ServiceScopeFactory { get; set; }
/// <summary>
/// Configuration
/// </summary>
private IConfiguration Configuration { get; set; }
/// <summary>
/// Logger
/// </summary>
private readonly ILogger<HostedMQTTService> _logger;
/// <summary>
/// Constructor
/// </summary>
/// <param name="configuration"></param>
public HostedMQTTService(IConfiguration configuration, ILogger<HostedMQTTService> logger, IServiceScopeFactory serviceScopeFactory)
{
this.PickByLight_Name = configuration.GetValue<string>("PickByLight_Name");
this.MqttURL = configuration.GetValue<string>("MQTTUrl");
this.MqttIsActive = configuration.GetValue<bool>("MQTTConnection");
this.ServiceScopeFactory = serviceScopeFactory;
this.Configuration = configuration;
this._logger = logger;
}
/// <summary>
/// Start the Task of the Background Service
/// </summary>
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Background-Service started...");
while (true)
{
try
{
//No Object is created
if (this.MqttClient == null)
{
_logger.LogInformation("Try to establishe new MQTT Client");
this.MqttClient = CreateNewMqttConnection();
}
else if (this.MqttClient.IsConnected == false)
{
_logger.LogInformation("MQTT Client is disconnected... Try to reconnect!");
this.MqttClient = CreateNewMqttConnection();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ein schwerwiegender Fehler im MQTT Background-Service ist aufgetreten.");
}
}
}
/// <summary>
/// Prints out all received messages
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Mqtt_Message_Received(object sender, MqttMsgPublishEventArgs e)
{
try
{
var agcMessage = Encoding.UTF8.GetString(e.Message);
_logger.LogInformation("Topic: " + e.Topic + " | Nachricht: " + agcMessage + " | QOS: " + e.QosLevel);
var resultString = Encoding.UTF8.GetString(e.Message);
MqttReadTopicClass mqttContent = JsonConvert.DeserializeObject<MqttReadTopicClass>(resultString);
using (var scope = this.ServiceScopeFactory.CreateScope())
{
var storageConfigurationManager = scope.ServiceProvider.GetService<IStorageConfigurationManager>();
var storageElementManager = scope.ServiceProvider.GetService<IStorageElementManager>();
this.StorageProcess = new MaterialStorageProcess(storageConfigurationManager, storageElementManager, this.Configuration);
StorageProcess.Remove(mqttContent.storageLocation);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Schwerwiegender Fehler beim Lesen von MQTT Nachrichten");
}
}
/// <summary>
/// Create new MQTT connection if connection is lost or doesn't exist
/// </summary>
private MqttClient CreateNewMqttConnection()
{
_logger.LogInformation("Create MQTT Client");
MqttClient client = new MqttClient(this.MqttURL, 32005, false, null, null, MqttSslProtocols.None);
string clientId = Guid.NewGuid().ToString();
client.MqttMsgPublishReceived += Mqtt_Message_Received;
client.Connect(clientId);
client.Subscribe(new string[] { "buttonpress_sepioo_pdi/" + this.PickByLight_Name }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
_logger.LogInformation("MQTT Client created");
return client;
}
/// <summary>
/// Stop the Task of the Background Service
/// </summary>
public async Task StopAsync(CancellationToken cancellationToken)
{
//Stop called without start
if (_executingTask == null)
{
return;
}
try
{
//Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
//wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
/// <summary>
/// Dispose the Background Service
/// </summary>
public void Dispose()
{
_stoppingCts.Cancel();
}
}
}
In my startup.cs File i am doing the following:
//Register Background Task
services.AddHostedService<HostedMQTTService>();
The problem is, that it seems to me that the hosted service is blocking the user-interface/webserver threads because i can not access the url of the .net 6 mvc application.
Could you give me a hint or a solution to this problem?
Thanks.
You will need to change your StartAsync-method to something like this:
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
_logger.LogInformation("Background-Service started...");
while (!cancellationToken.IsCancellationRequested)
{
try
{
//No Object is created
if (this.MqttClient == null)
{
_logger.LogInformation("Try to establish new MQTT Client");
this.MqttClient = CreateNewMqttConnection();
}
else if (this.MqttClient.IsConnected == false)
{
_logger.LogInformation("MQTT Client is disconnected... Try to reconnect!");
this.MqttClient = CreateNewMqttConnection();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ein schwerwiegender Fehler im MQTT Background-Service ist aufgetreten.");
}
}
});
}
You are blocking the process, because your hosted service never starts.
Remove the while(true) loop from the Start method.
Ensure base.StartAsync(cancellationToken); is always called.
Edit: I saw you implement the IHostedService interface. Try to inherits from the BackgroundService class
Related
I just created a chat App using signalR in C#(server) and JS(Client) and every thing is good and there is no problem when I run the server and client in my same device (localhost)
Now I want to run the client app out side my network , for example I want to make a chat with my friend who is living in another country using My app ,but I don't now how to do that and from where I need to start my search to do so.
Pleas if any one know how to do that or if any one know source that I can learn from it Help me
This is My signalR server Code
namespace PmmTcpListener
{
public class PmmSignalRServer : IDisposable
{
#region Private Properties
/// <summary>
/// To be set in <see cref="Dispose(bool)"/> method
/// </summary>
private bool _disposedValue;
public string SignalRwebAddress = "https://localhost:5050";
public bool IsStarted = false;
public bool cIsStarted = false;
public string cSignalRwebAddress = "https://localhost:5050/pmmchat";
private string PrevMessage = "";
private string CrntMessage = "";
private string ClientUrl = "";
#endregion
#region Constructor
/// <summary>
/// The main constructor for the class
/// Doing instantiation for Sql Commands based on database type <see cref="DbTypes"/>
/// By the way the default selected one is type <see cref="DbTypes.mysql"/>
/// </summary>
public PmmSignalRServer()
{
//Initialize the Sql Commands
//Sqlcmd = new MySqlCommand();
//AlarmsCommand = new MySqlCommand();
//SqlServerCmd = new SqlCommand();
//SqlServerAlarmsCommand = new SqlCommand();
//SqliteCmd = new SQLiteCommand();
//SqliteAlarmsCommand = new SQLiteCommand();
}
#endregion
#region MainFunctions
/// <summary>
/// To be set
/// </summary>
//public void Start(string SRport = "https://localhost:5050")
//{
// CreateWebHostBuilder().Build().Run();
//}
//private static IWebHostBuilder CreateWebHostBuilder() => WebHost.CreateDefaultBuilder().UseStartup<iStartup>().UseUrls("https://localhost:5050");
/// <summary>
/// To be set
/// </summary>
public void StartServe(string SRport = "http://localhost:5050")
{
SignalRwebAddress = SRport;
IsStarted = true;
Thread Serverthread = new Thread(StartSeerverThread);
Serverthread.Start();
}
/// <summary>
/// To be set
/// </summary>
private void StartSeerverThread()
{
try
{
WebHost.CreateDefaultBuilder()
.ConfigureServices(services => services.AddSignalR())
.ConfigureServices(services => services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
//builder.AllowAnyMethod().AllowAnyHeader()
// .WithOrigins("http://localhost:59054")
// .AllowCredentials();
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
})))
.Configure(app => app.UseRouting().UseCors("CorsPolicy").UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/PmmChat");
})
)
.UseUrls(SignalRwebAddress)
.ConfigureLogging(config =>
{
config.ClearProviders();
})
.Build()
.Run();
}
catch (Exception ex)
{
PmmGlobFunctions.TrackThis(" StartServerThread : " + ex.Message);
}
}
/// <summary>
/// To be set
/// </summary>
public void StartClient(string hubUrl = "https://localhost:5050")
{
SignalRwebAddress = hubUrl;
cIsStarted = true;
//ClientUrl
ClientUrl = SignalRwebAddress.Replace(#"*", "localhost");
ClientUrl = ClientUrl + #"/pmmchat";
Thread thread = new Thread(StartClientThread);
thread.Start();
}
/// <summary>
/// To be set
/// </summary>
private void StartClientThread()
{
try
{
//IsStarted = true; .WithUrl(SignalRwebAddress+"/pmmchat")
var connection = new HubConnectionBuilder().WithUrl(ClientUrl).Build();
connection.StartAsync().Wait();
connection.InvokeCoreAsync("SendMessage", args: new[] { "PMM Users" }); // join the main group "PMM Users"
while (cIsStarted)
{
if (CrntMessage != PrevMessage)
{
connection.InvokeCoreAsync("SendMessage", args: new[] { "PmmServer", CrntMessage });
PrevMessage = CrntMessage;
}
}
connection.DisposeAsync();
}
catch (Exception ex)
{
PmmGlobFunctions.TrackThis(" StartClientThread : " + ex.Message);
}
}
/// <summary>
/// To be set
/// </summary>
public void StopClient()
{
cIsStarted = false;
}
/// <summary>
/// To be set
/// </summary>
public void SendFromServer(string message)
{
CrntMessage = message;
//connection.On("ReceiveMessage", (string userName, string message) =>
//{
// Console.WriteLine(userName + " : " + message);
//});
}
#endregion
#region Implement Dispose
/// <summary>
/// if <paramref name="disposing"/> = true, The dispose method has been called
/// by our code. Managed and unmanaged objects can be disposed
/// if <paramref name="disposing"/> = false, The dispose method has been called
/// by runtime finalizer. Unmanaged objects only should be disposed.
/// </summary>
/// <param name="disposing">disposing value</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// Dispose any managed objects
// ...
}
// Now disposed of any unmanaged objects
// ...
_disposedValue = true;
}
}
/// <summary>
/// Dispose method which implemented by <see cref="IDisposable"/>
/// </summary>
public void Dispose()
{
// Calling override Dispose method to be used in implemented method
Dispose(true);
// Commented by Dr.Mahmoud
//GC.SuppressFinalize(this);
}
#endregion
}
public class ConnectionMapping<T>
{
private readonly Dictionary<T, HashSet<string>> _connections =
new Dictionary<T, HashSet<string>>();
public int Count
{
get
{
return _connections.Count;
}
}
public void Add(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
connections = new HashSet<string>();
_connections.Add(key, connections);
}
lock (connections)
{
connections.Add(connectionId);
}
}
}
public IEnumerable<string> GetConnections(T key)
{
HashSet<string> connections;
if (_connections.TryGetValue(key, out connections))
{
return connections;
}
return Enumerable.Empty<string>();
}
public void Remove(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
return;
}
lock (connections)
{
connections.Remove(connectionId);
if (connections.Count == 0)
{
_connections.Remove(key);
}
}
}
}
}
public class ChatHub : Hub
{
// My Work --> Mohannad
private readonly static ConnectionMapping<string> _connections = new ConnectionMapping<string>();
public override Task OnConnectedAsync()
{
Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
return base.OnConnectedAsync();
}
public async Task SendMessageToClient(string user, string receiverConnectionId, string message)
{
foreach (var connectionId in _connections.GetConnections(user))
{
await Clients.Client(connectionId).SendAsync("ReceiveMessage", user, message);
}
// await Clients.Client(receiverConnectionId).SendAsync("ReceiveMessage", user, message);
}
public async Task SendMessageUsers(string user, string[] receiverUsersIds, string message)
{
// here you need to set the list of Users
await Clients.Users(receiverUsersIds).SendAsync("ReceiveMessage", user, message);
}
public async Task SendMessageOthers(string user, string message)
{
await Clients.Others.SendAsync("ReceiveMessage", user, message);
}
public async Task SendMessageToUser(string user, string receiverConnectionId, string message)
{
await Clients.Client(receiverConnectionId).SendAsync("ReceiveMessage", user, message);
}
public string GetConnectionId() => Context.ConnectionId;
//public override Task OnConnectedAsync()
//{
// string name = Context.User.Identity.Name;
// _connections.Add(name, Context.ConnectionId);
// return base.OnConnectedAsync();
//}
//public override Task OnDisconnectedAsync(Exception exception)
//{
// string name = Context.User.Identity.Name;
// _connections.Remove(name, Context.ConnectionId);
// return base.OnDisconnectedAsync(exception);
//}
// end Mohannad
public async Task SendMessage(string user, string message)
{
// await Clients.All.SendAsync("ReceiveMessage", user, PmmCommandReader.ReadCommand(message));
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("PMM Users").SendAsync("ReceiveMessage", user, message);
}
public Task JoinGroup(string groupName)
{
return Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
}
//End of NameSpace
}
I have been struggling on this issue for weeks now.
I have an app where i have configured owin backend with web api and autofac DI with background handfire jobs. I have alsmost looked at every question on Stackoveflow regarding this but nothing seems to work.
My app regarding OWIN/Hangfire/WebAPI all seems to work okay. Until it comes to SignalR push messages.
If i call any notification hub endpoint from js client push messages go okay and i can receive push messages on any other connected client. But when i wan to send message from my api controller or hangfire job it never reaches to any client.
Startup.cs
public void Configuration(IAppBuilder app)
{
//var signalRHelper = new SignalRHelper(GlobalHost.ConnectionManager.GetHubContext<NotificationHub>());
var constants = new Constants();
constants.Set(ConstantTypes.AllyHrNoReplyEmailAddress, Util.Constants.AllyHrNoReplyEmailAddress);
constants.Set(ConstantTypes.SendGridKey, Util.Constants.SendGridKey);
constants.Set(ConstantTypes.EncryptionKey, Util.Constants.EncryptionKey);
constants.Set(ConstantTypes.ApiUrl, Util.Constants.ApiUrl);
constants.Set(ConstantTypes.RootFolder, Util.Constants.RootFolder);
constants.Set(ConstantTypes.FrontEndUrl, Util.Constants.FrontEndUrl);
GlobalConfiguration.Configuration
.UseSqlServerStorage("AllyHrDb");
var config = System.Web.Http.GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
var jobBuilder = new ContainerBuilder();
var signalRBuilder = new ContainerBuilder();
var hubConfig = new HubConfiguration();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.Register(x => constants);
builder.RegisterModule(new ServiceModule());
jobBuilder.Register(x => constants);
jobBuilder.RegisterModule(new HangfireServiceModule());
signalRBuilder.RegisterModule(new SignalRServiceModule());
signalRBuilder.Register(x => constants);
signalRBuilder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
signalRBuilder.RegisterType<ConnectionManager>().As<IConnectionManager>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<NotificationHub>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<SignalRHelper>().PropertiesAutowired().ExternallyOwned().SingleInstance();
signalRBuilder.Register(context => context.Resolve<IDependencyResolver>().Resolve<IConnectionManager>().GetHubContext<NotificationHub, INotificationHub>()).ExternallyOwned().SingleInstance();
var hubContainer = signalRBuilder.Build();
builder.RegisterInstance(hubContainer.Resolve<IConnectionManager>());
builder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
builder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
builder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
jobBuilder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
jobBuilder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
jobBuilder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
var container = builder.Build();
var jobContainer = jobBuilder.Build();
var idProvider = new SignalRCustomUserIdProvider();
hubConfig.Resolver = new AutofacDependencyResolver(hubContainer);
hubConfig.Resolver.Register(typeof(IUserIdProvider), () => idProvider);
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
map.RunSignalR(hubConfig);
});
GlobalConfiguration.Configuration.UseAutofacActivator(jobContainer);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseHangfireServer();
app.UseHangfireDashboard();
ConfigureAuth(app);
app.UseWebApi(config);
}
I had to use different container because i have db set to InstancePerRequest scope.
All my services are being resolved in notification hub class, no problems there. The only issues is when i try and send message from hangfire service or even from api controller using hub context it never reaches to any client.
NotificationHub.cs
public interface INotificationHub
{
/// <summary>
///
/// </summary>
void pushNotification(string message);
/// <summary>
///
/// </summary>
/// <param name="model"></param>
void getNotification(object model);
void getMessage(object model);
}
/// <summary>
/// Notification Hub
/// </summary>
[HubName("NotificationHub")]
[Authorize]
public class NotificationHub : Hub<INotificationHub>
{
/// <summary>
///
/// </summary>
public static IHubContext<INotificationHub> GlobalContext { get; private set; }
private readonly IChatMessagingService _chatMessagingService;
private readonly IUserService _userService;
private Guid LoggedInUserId
{
get
{
var claims = ((ClaimsIdentity)Context.User.Identity).Claims.ToArray();
var userIdClaim = claims.FirstOrDefault(x => x.Type.Equals("UserId"));
if (userIdClaim == null) return Guid.Empty;
return Guid.Parse(userIdClaim.Value);
}
}
/// <summary>
/// Consructor
/// </summary>
/// <param name="lifetimeScope"></param>
/// <param name="context"></param>
public NotificationHub(ILifetimeScope lifetimeScope, IHubContext<INotificationHub> context)
{
GlobalContext = context;
try
{
var childScope = lifetimeScope.BeginLifetimeScope();
_chatMessagingService = childScope.Resolve<IChatMessagingService>();
_userService = childScope.Resolve<IUserService>();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
/// <summary>
/// Notifications
/// </summary>
public void Notifications()
{
Clients.All.pushNotification("AllyHr" + LoggedInUserId);
}
/// <summary>
/// Send Message
/// </summary>
/// <param name="model"></param>
public void SendMessage(SendChatMessageBindingModel model)
{
var chatMessage = _chatMessagingService.SendMessageToGroup(LoggedInUserId, model.GroupId, model.Message);
var recipientIds = _chatMessagingService.GetChatMembersByGroupId(LoggedInUserId, model.GroupId);
var stringUserIds = new List<string>();
var chatGroup = _chatMessagingService.GetChatGroupById(model.GroupId);
foreach (var recipientId in recipientIds)
{
stringUserIds.Add(recipientId.ToString());
}
Clients.Users(stringUserIds).getNotification(new
{
message = "A new Message is Recieved in Chat Group: " + chatGroup.Name,
groupId = chatGroup.Id
});
var chatMessageVm = chatMessage.Map<ChatMessage, ChatMessageViewModel>();
chatMessageVm.Sender = _userService.Get(chatMessageVm.SenderId).Map<User, UserViewModel>();
stringUserIds.Add(LoggedInUserId.ToString());
Clients.Users(stringUserIds).getMessage(chatMessageVm);
}
}
signalRhelper.cs use to call from api or from Hangfire services
public class SignalRHelper
{
public IConnectionManager ConnectionManager { get; set; }
public IHubContext<INotificationHub> HubContext { get; set; }
/// <summary>
/// Send Notifications to Users
/// </summary>
/// <param name="message"></param>
/// <param name="userIds"></param>
public void GetNotification(object message, IList<string> userIds)
{
HubContext.Clients.Users(userIds).getNotification(message);
}
/// <summary>
/// Get LoggedInUser Id for SignalR
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static Guid GetLoggedInUserId(IPrincipal user)
{
var claim = GetLoggedinUserClaim(user);
if (claim == null) return Guid.Empty;
return Guid.Parse(claim.Value);
}
private static Claim GetLoggedinUserClaim(IPrincipal user)
{
var claim = ((ClaimsIdentity)user.Identity).Claims.ToArray();
return claim.FirstOrDefault(x => x.Type.Equals("UserId"));
}
}
Could this be related to Autofac creating a new lifetimescope for your call, but you were expecting to continue using the existing scope? Maybe check your autofac registrations for singleinstance / instanceperlifetimescope
Just saying, but have you registered any static classes? They can keep your scope alive for far too long.
I see you're using multiple containerbuilders - that's not something we do over here, we have one 'massive' containerbuilder for each app. I'm curious why you're doing that? To satisfy my curiosity, could you try using a single containerbuilder and registering everything on that single builder? (Although it looks like this is a pattern for SignalR and autofac)
The documentation says: " a common error in OWIN integration is the use of GlobalHost."
It looks like you're doing exactly that.
I have an odata/webapi app that's been updated to use OWIN auth. The code to issue tokens (/token url) works fine and returns a token, but any "normal" call to any controllers returns HTTP 400
{"error": "unsupported_grant_type"}
with no additional information.
Startup.cs:
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Web.Http;
[assembly: OwinStartup(typeof(VP4C_API.Startup))]
namespace VP4C_API
{
public class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthBearerAuthentication(oAuthBearerAuthenticationOptions);
Statics.Config = new HttpConfiguration();
WebApiConfig.Register(Statics.Config);
app.UseWebApi(Statics.Config);
}
/// <summary>
/// Called at when the AppPool starts the website
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
AuthorizationProvider which returns the token:
using System;
using System.Threading.Tasks;
using Microsoft.Owin.Security.OAuth;
using Microsoft.Owin.Security;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
namespace VP4C_API
{
internal class SimpleAuthorizationServerProvider : IOAuthAuthorizationServerProvider
{
Task IOAuthAuthorizationServerProvider.AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Take the ClientID and validate its one we want to talk to
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateTokenRequest"/> />
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
if (context.ClientId == "App1Test")
{
var identity = new ClaimsIdentity(new GenericIdentity(
context.ClientId, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("urn:oauth:scope", x))
);
context.Validated(identity);
}
else
{
context.Rejected();
context.SetError("Unknown client id", "You must list the client id in the known clients table");
}
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.GrantCustomExtension(OAuthGrantCustomExtensionContext context)
{
throw new NotImplementedException();
}
Task IOAuthAuthorizationServerProvider.GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateTokenRequest"/>
/// Check the username/password here
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName == "test" && context.Password == "test")
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
System.Security.Principal.GenericIdentity gi = new System.Security.Principal.GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType);
System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity(gi, context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
var ticket = new AuthenticationTicket(ci, null);
context.Validated(ticket);
}
else
{
context.SetError("GrantResourceOwner Error", "Username or Password is incorrect.");
context.Rejected();
}
return Task.FromResult(0);
}
/// <summary>
/// Called first to filter on the endpoint attributes (e.g. client application IP address)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.MatchEndpoint(OAuthMatchEndpointContext context)
{
context.MatchesTokenEndpoint();
return Task.FromResult(0);
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.GrantResourceOwnerCredentials"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.TokenEndpoint(OAuthTokenEndpointContext context)
{
AuthenticationProperties ap = new AuthenticationProperties();
ap.AllowRefresh = true;
ap.IssuedUtc = DateTime.UtcNow;
ap.IsPersistent = true;
context.Issue(context.Identity, ap);
return Task.FromResult(0);
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.TokenEndpoint"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.MatchEndpoint"/>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientID;
string clientPassword;
context.TryGetBasicCredentials(out clientID, out clientPassword);
context.Validated(clientID);
return Task.FromResult(0);
}
Task IOAuthAuthorizationServerProvider.ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
throw new NotImplementedException();
}
/// <summary>
/// Called after <see cref="IOAuthAuthorizationServerProvider.ValidateClientAuthentication"/>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task IOAuthAuthorizationServerProvider.ValidateTokenRequest(OAuthValidateTokenRequestContext context)
{
context.Validated();
return Task.FromResult(0);
}
}
}
And a sample controller snippet for http://host/api/Verify/1
[HttpGet, AllowAnonymous, Route("{companyID}")]
public IHttpActionResult Verify(int companyID)
{
string cs = "not set";
int check = -1;
try
{
ErrorLogging.Logging.Information(null, null, Request.RequestUri, $"Verify Start company {companyID}");
Any suggestions on where to look?
I would like to implement a HTTPS connection for my rest webservice.
The HTTP version works as well, but when i try to connect over HTTPS and send a XML file or something else, It already fails when establishing the connection via https.
Has someone an idea what i can change to test it over https?
Startup.cs:
using System;
using Owin;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
[assembly: OwinStartup(typeof(SimuXmlDcs.MsiWebServer.Startup))]
namespace SimuXmlDcs.MsiWebServer
{
using System.Configuration;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Web.Http;
using Microsoft.Owin.Security;
using Newtonsoft.Json;
using SimuXmlDcs.MsiWebServer.App_Start;
using SimuXmlDcs.MsiWebServer.Controllers;
/// <summary>
/// The startup.
/// </summary>
public class Startup
{
/// <summary>
/// The configuration.
/// </summary>
/// <param name="app">
/// The app.
/// </param>
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(name: "SystemAction", routeTemplate: "api/{controller}/{system}/{action}", defaults: new { action = RouteParameter.Optional });
config.Routes.MapHttpRoute(name: "System", routeTemplate: "api/{controller}/{system}");
config.Routes.MapHttpRoute(name: "Info", routeTemplate: "api/{controller}");
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.XmlFormatter.UseXmlSerializer = true;
app.UseWebApi(config);
//byte[] test = new byte[4];
//test[0] = 10;
//test[1] = 78;
//test[2] = 2;
//test[3] = 193;
//IPAddress ipaddress = new IPAddress(test);
//TcpListener server = new TcpListener(ipaddress, 8443);
//server.Start();
//TcpClient client = server.AcceptTcpClient();
//SslStream stream = new SslStream(client.GetStream(), false, VerifyClientCertificate, null);
}
private static bool VerifyClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
/// <summary>
/// Setup authorization server
/// </summary>
/// <param name="app">
/// The app.
/// </param>
private void ConfigureOAuth(IAppBuilder app)
{
int timeSpan;
AppSettingsReader asr = new AppSettingsReader();
int.TryParse(asr.GetValue("TokenExpireInMinutes", typeof(string)).ToString(), out timeSpan);
app.UseOAuthAuthorizationServer(
new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = !MsiRestServer.UseHttps,
TokenEndpointPath = new PathString("/api/getsecuretoken"),
AccessTokenExpireTimeSpan = timeSpan != 0 ? TimeSpan.FromMinutes(timeSpan) : TimeSpan.FromDays(1),
Provider = new AuthorizationServerProvider(),
ApplicationCanDisplayErrors = true
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
AuthorizationServerProvider
namespace SimuXmlDcs.MsiWebServer.App_Start
{
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using SimuXmlDcs.MsiWebServer.Models;
/// <summary>
/// The authorization server provider.
/// </summary>
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
/// <summary>
/// The validate client authentication.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
/// <summary>
/// The grant resource owner credentials.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.Password != "password")
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Role, RoleName.Admin));
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
context.Validated(new AuthenticationTicket(identity, new AuthenticationProperties { }));
}
}
}
MsiRestServer
namespace SimuXmlDcs.MsiWebServer
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using log4net;
using Microsoft.Owin.Hosting;
/// <summary>
/// The msi rest server.
/// </summary>
public static class MsiRestServer
{
private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static Thread msiWebServer;
private static bool endServer = false;
/// <summary>
/// Gets or sets a value indicating whether use https.
/// </summary>
public static bool UseHttps { get; set; }
/// <summary>
/// Gets or sets the base address.
/// </summary>
public static string BaseAddress { get; set; } = "https://test2234:8443";
/// <summary>
/// The startup server.
/// </summary>
public static void StartupServer()
{
Thread.Sleep(200);
endServer = false;
msiWebServer = new Thread(ServerThread);
msiWebServer.Start();
}
/// <summary>
/// The stop server.
/// </summary>
public static void StopServer()
{
endServer = true;
}
/// <summary>
/// The server thread.
/// </summary>
private static void ServerThread()
{
try
{
Uri tstAddress = new Uri(BaseAddress);
//WebServiceHost svcHost = new WebServiceHost();
// Start OWIN host
using (WebApp.Start<Startup>(url: BaseAddress))
{
while (!endServer)
{
Thread.Sleep(250);
}
}
}
catch (Exception ex)
{
logger.Error(ex);
MessageBox.Show(ex.Message);
}
}
}
}
For a self hosted app you must create a certificate.
http://chavli.com/how-to-configure-owin-self-hosted-website-with-ssl/
But when you use an IIS you only need to enable the project property "SSL enabled".
I'm new to this forum and to have a question about await/async use in Xamarin (Also the first time I work with).
I am working for my internship on a project using Xamarin, PCL, MvvmCross.
In my PCL im do a postrequest to a WCF service to login in my application. In WP8 everything just works fine, but when I am running my application on Android the response is always null.
Below you can find my httpclient class. The method with the post is InternalPostAsync
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using Anton.Mobile.Shared.Infrastructure;
using System.Net;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
namespace Anton.Mobile.Shared.Data
{
public class AntonClient
{
#region static
/// <summary>
/// Base Uri of the Ria service (e.g. http://example.com/)
/// </summary>
private static readonly Uri _baseUri = new Uri(Config.BaseUri);
/// <summary>
/// Last cookie response header (Session, authentication, ...)
/// </summary>
private static string _cookieHeader = null;
/// <summary>
/// Lock object for read/write <para>_cookieHeader</para>
/// </summary>
private static object _lockObj = new object();
#endregion
#region protected
/// <summary>
/// Creates a client
/// </summary>
/// <param name="container">Cookie container to use</param>
/// <returns>HttpClient</returns>
protected virtual HttpClient CreateClient(CookieContainer container)
{
//set container on handler for tracking cookies between request-response
var handler = new HttpClientHandler()
{
CookieContainer = container,
UseCookies = true,
UseDefaultCredentials = false,
};
//Create client and set the base address
var cl = new HttpClient(handler)
{
BaseAddress = _baseUri
};
if (!string.IsNullOrEmpty(_cookieHeader))
{
cl.DefaultRequestHeaders.Add("Cookies", _cookieHeader);
}
return cl;
}
/// <summary>
/// Creates a JSON content request
/// </summary>
/// <param name="jsonContent">JSON value</param>
/// <returns>JSON content</returns>
protected virtual HttpContent CreateRequestContent(string jsonContent)
{
var content = new StringContent(jsonContent,Encoding.UTF8,"application/json");
//content.Headers.Add("ContentType", "application/json");
return content;
}
/// <summary>
/// Save cookies <para>_cookieHeader</para>
/// </summary>
/// <param name="container">cookie container</param>
protected void ParseCookies(HttpResponseMessage msg)
{
IEnumerable<string> values;
if (!msg.Headers.TryGetValues("Set-Cookie", out values) || !values.Any())
return;
//var cookies = container.GetCookieHeader(_baseUri);
var cs = new List<string>();
foreach (var v in values)
{
string[] vs = v.Split(new char[] { ';' });
string[] value = vs[0].Split(new char[] { '=' });
container.Add(new Uri("Http://initesting"), new Cookie(value[0], value[1]));
cs.Add(string.Format("{0}={1}", value[0], value[1]));
}
lock (_lockObj)
{
_cookieHeader = string.Join(";", cs.ToArray());
}
}
private static CookieContainer container = new CookieContainer();
/// <summary>
/// Create a new cookie container from <para>_cookieHeaders</para>
/// </summary>
/// <returns>Cookie container</returns>
protected CookieContainer CreateCookieContainer()
{
//lock (_lockObj)
//{
// if (!string.IsNullOrEmpty(_cookieHeader))
// {
// foreach (var header in _cookieHeader.Split(new char[] { ';' }))
// {
// container.SetCookies(_baseUri, header);
// }
// }
//}
return container;
}
/// <summary>
/// Executes a POST HTTP Request
/// </summary>
/// <param name="jsonContent">POST JSON content</param>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalPostAsync(string jsonContent, Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
var content = CreateRequestContent(jsonContent);
var response = await client.PostAsync(uri, content);
if (response.StatusCode != HttpStatusCode.OK)
{
return null; //todo
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
/// <summary>
/// Executes a GET HTTP Request
/// </summary>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalRequestAsync(Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
HttpResponseMessage response = await client.GetAsync(uri);
if (response.StatusCode != HttpStatusCode.OK)
{
return null;
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
#endregion protected
#region public
/// <summary>
/// Executes a POST HTTP Request for a given Request key
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <typeparam name="TResult">Result Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="key">Unique Request Key</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResult> PostAsync<TRequest, TResult>(TRequest request, RequestKey key)
where TRequest : class
where TResult : class
{
try
{
var uri = RequestMap.GetUri(key);
string jsonResult = await InternalPostAsync(request.SerializeJson(), uri);
return jsonResult.DeserializeJson<TResult>();
}
catch (Exception)
{
//todo
}
return default(TResult);
}
/// <summary>
/// Executes a POST HTTP Request for a given service uri
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="uri">Service URI</param>
/// <returns>Deserialized POST response content of type dynamic</returns>
public async Task<dynamic> PostAsync<TRequest>(TRequest request, string uri)
{
try
{
string jsonResult = await InternalPostAsync(request.SerializeJson(), new Uri(uri, UriKind.Absolute));
return jsonResult.DynamicJson();
}
catch (Exception)
{
//todo
}
return null;
}
/// <summary>
/// Executes a GET HTTP Request for a givin key and query string parameter info
/// </summary>
/// <typeparam name="TResponse">Response Type</typeparam>
/// <param name="key">Unique request key</param>
/// <param name="queryString">Querystring info</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResponse> RequestAsync<TResponse>(RequestKey key, IDictionary<string, string> queryString = null)
{
try
{
string jsonResult = await InternalRequestAsync(RequestMap.GetUri(key, queryString));
var dynamicResult = jsonResult.DynamicJson();
var item = (dynamicResult as JObject)[RequestMap.GetValue(key) + "Result"]["RootResults"].First; //todo: better solution for this
return item.ToObject<TResponse>();
}
catch (Exception)
{
//todo
}
return default(TResponse);
}
#endregion public
}
}
Regarding the website of Xamarin async/await is supported, but I did not find any similar problems. I hope you can help me.
I have solved my problem.
In Android, when you add the NuGet package with the HTTP Libraries, something went wrong when adding the references. You have to add the references mannualy to solve it. I also editted my Uri, now it works with the IP-address in the Uri.