I have a running multichannelapplication connceted via wcf service to a sqlserver 2012. When I stopp the service the app keep running and the data is stored in the entity manager of breeze:
(function () {
var oldClient = OData.defaultHttpClient;
var myClient = {
request: function (request, success, error) {
if (request.requestUri.indexOf("$metadata", request.requestUri.length - "$metadata".length) !== -1) {
request.headers.Accept = "application/xml";
}
return oldClient.request(request, success, error);
}
};
OData.defaultHttpClient = myClient;
breeze.config.initializeAdapterInstance("dataService", "OData", false);
var dataNS = DevExpress.data;
var manager = new breeze.EntityManager({
dataService: new breeze.DataService({
serviceName: "http://localhost:57049/DataService.svc",
hasServerMetadata: false,
adapterName: "OData"
})
});
App.db = {
tblInvoice: new dataNS.BreezeStore({
entityManager: manager,
resourceName: "tblInvoice",
autoCommit: true,
}),
when i restart the service the data should be synchronized but it doesn't do this automaticly. The Breeze api says saveChanges() to save to entity manager. How to sync the entities with the server if the service is available again?
Related
I have created an Azure SignalR (Serverless) reosurce in azure portal.
Then I have created an azure function HttpTrigger locally that references Microsoft.Azure.WebJobs.Extensions.SignalRService. In my azure function I have this code:
`public static class HttpTrigger
{
[FunctionName("Negotiate")]
public static SignalRConnectionInfo Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[SignalRConnectionInfo(HubName = "notificationHub")] SignalRConnectionInfo connectionInfo,
ILogger log)
{
log.LogInformation("Returning connection: " + connectionInfo.Url + "" + connectionInfo.AccessToken);
return connectionInfo;
}
[FunctionName("Notify")]
public static async Task<IActionResult> Notify([HttpTrigger(AuthorizationLevel.Function, "get", Route=null)] HttpRequest req,
[SignalR(HubName = "notificationHub")] IAsyncCollector<SignalRMessage> signalRMessage,
ILogger log)
{
log.LogInformation("Notify");
string msg = string.Format("Message from agent! {0} ", DateTime.Now);
await signalRMessage.AddAsync(
new SignalRMessage
{
Target = "notifications",
Arguments = new[] { msg }
});
return new OkObjectResult("ok");
}
}
`
Also in my azure function, this is what my local.settings.json looks like:
`
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureSignalRConnectionString": "myconnstringhere"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "http://localhost:53377",
"CORSCredentials": true
}
}
To also solve the CORS problem, I have added http://localhost:53377 domain of my client part project.
My client part is a separate asp.net web application project . So here I am connecting to this azure function like this:
`
<script>
$(document).ready(function(){
const connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7071/api/")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.onclose(start);
start(connection);
});
async function start(connection){
try {
await connection.start();
console.log("SignalR connected.");
connection.on("notifications", (message) => {
$("#detailcontainer").html(message);
console.log(message)
});
}
catch(err) {
console.log(err);
}
}
</script>
Now, I have published my azure function. But now it is not working anymore. It gets an error saying unauthorized when triggering /api/negotiate.
My azure function is a .net 6 project while the client app is a net framework 4.8. Is this because my client app is still in webforms?
I have added the connection string of my azure signalR to the application settings having a name format like this: Azure__SignalR__ConnectionString
I also have configured CORS allowed origins for my azure function, I added my client localhost app.
Closing this one because I found the answer. And it was really annoying that I have missed this one out.
I replaced AuthorizationLevel.Function to AuthorizationLevel.Anonymous. Because I am just passing the domain/api of my azure function and letting the signalR do its thing on their JS.
Using the facebook login authentication in angular app with identity server 4. On logout method PostLogoutRedirectUri , ClientName, LogoutId is always null.
private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
{
// get context information (client name, post logout redirect URI and iframe for federated signout)
var logout = await _interaction.GetLogoutContextAsync(logoutId);
var vm = new LoggedOutViewModel
{
AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
SignOutIframeUrl = logout?.SignOutIFrameUrl,
LogoutId = logoutId
};
if (User?.Identity.IsAuthenticated == true)
{
var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider)
{
var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
if (providerSupportsSignout)
{
if (vm.LogoutId == null)
{
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// before we signout and redirect away to the external IdP for signout
vm.LogoutId = await _interaction.CreateLogoutContextAsync();
}
vm.ExternalAuthenticationScheme = idp;
}
}
}
return vm;
}
Angular oidc clident code
logout(): Promise<any> {
return this._userManager.signoutRedirect();
}
Client setup
public IEnumerable<Client> GetClients()
{
var client = new List<Client>
{
new Client
{
ClientId = ConstantValue.ClientId,
ClientName = ConstantValue.ClientName,
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = { string.Format("{0}/{1}", Configuration["IdentityServerUrls:ClientUrl"], "assets/oidc-login-redirect.html"), string.Format("{0}/{1}", Configuration["IdentityServerUrls:ClientUrl"], "assets/silent-redirect.html") },
PostLogoutRedirectUris = { string.Format("{0}?{1}", Configuration["IdentityServerUrls:ClientUrl"] , "postLogout=true") },
AllowedCorsOrigins = { Configuration["IdentityServerUrls: ClientUrl"] },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
ConstantValue.ClientDashApi
},
IdentityTokenLifetime=120,
AccessTokenLifetime=120
},
};
return client;
}
logoutId is always null. I am successfully able to login to facebook return to the callback method. But redirect uri is always null.
Reference
IdentityServer4 PostLogoutRedirectUri null
This may not be your issue, but it was my issue when I got the same error as you so I am posting my own experience here.
I was following along in a Pluralsight video that was constructing an Angular app using IdentityServer4 as the STS Server, and it directed me to set the post_logout_redirect_uri in the configuration for my UserManager in the AuthService I was constructing, like so:
var config = {
authority: 'http://localhost:4242/',
client_id: 'spa-client',
redirect_uri: `${Constants.clientRoot}assets/oidc-login-redirect.html`,
scope: 'openid projects-api profile',
response_type: 'id_token token',
post_logout_redirect_uri: `${Constants.clientRoot}`,
userStore: new WebStorageStateStore({ store: window.localStorage })
}
this._userManager = new UserManager(config);
An old issue at the github repo https://github.com/IdentityServer/IdentityServer4/issues/396 discussed the fact that this is set automatically now and doesn't need to be set explicitly (see the end of the thread). Once I removed that from the configuration I no longer had the issue where logoutId was null in the AccountController's Logout method:
/// <summary>
/// Show logout page
/// </summary>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
So this was the correct setup for the config for me:
var config = {
authority: 'http://localhost:4242/',
client_id: 'spa-client',
redirect_uri: `${Constants.clientRoot}assets/oidc-login-redirect.html`,
scope: 'openid projects-api profile',
response_type: 'id_token token',
userStore: new WebStorageStateStore({ store: window.localStorage })
}
this._userManager = new UserManager(config);
I had a similar issue and for a few hours I was lost. In my case the value/url I had in angular for post_logout_redirect_uri (in the UserManagerSettings) was different than the value/url I had in my IdentityServer4 in the field PostLogoutRedirectUris of the Client configuration. I messed up the routes. It's a silly mistake but sometimes you miss the simple things.
I have built a RESTful web service using ASP.NET HttpHandler, when running the web service project im redirected to the default page from which I can choose to download the DOJO code for my Client app.
here is a code snippet from the downloaded file:
function PickrWebService(){ self = this; }
PickrWebService.prototype = {
self: null,
urlString: "http://AYMAN/Handler.ashx",
CreateUser:function(Email,Username,Password,FirstName,Surname,Birth,Gender,Mobile,Picture,Address,successFunction,failFunction,token) {
var data = { 'interface': 'PickrWebService', 'method': 'CreateUser', 'parameters': {'Email':Email,'Username':Username,'Password':Password,'FirstName':FirstName,'Surname':Surname,'Birth':Birth,'Gender':Gender,'Mobile':Mobile,'Picture':Picture,'Address':Address}, 'token': token };
var jsonData = dojo.toJson(data);
var xhrArgs = {
url: self.urlString,
handleAs: 'json',
postData: jsonData,
load: successFunction,
error: failFunction };
var deferred = dojo.xhrPost(xhrArgs);
},
CheckUserExistence:function(Email,successFunction,failFunction,token) {
var data = { 'interface': 'PickrWebService', 'method': 'CheckUserExistence', 'parameters': {'Email':Email}, 'token': token };
var jsonData = dojo.toJson(data);
var xhrArgs = {
url: self.urlString,
handleAs: 'json',
postData: jsonData,
load: successFunction,
error: failFunction };
var deferred = dojo.xhrPost(xhrArgs);
}
}
I need help on how to use this code in my client app, and what does the parameter 'token' refer to?
The code is a javascript object for you service which you can call the webservice, by invoking the methods. token is not the part of dojo.xhrPost, it might be from the ASP.Net for passing authentication token. If you have not setup the security on the service, you could ignore it.
var successFunction = function(args){
//Handle the success response.
}
var failFunction= function(err){
//Handle the failure response.
}
var service = new PickrWebService();
service.createUser(Email,Username,Password,
FirstName,Surname,Birth,Gender,Mobile,Picture,Address,successFunction,failFunction);
Apart from the above code, you need to add the dojo api in you client.
I have an UI app on port 8001 and an app named contract on port 7001. I have 'cluster' installed and working. I have a subscription and insert method defined in 'contract' app.
'contract' server/app.js
Cluster.connect("mongodb://<username>:<passwd>#URL");
var options = {
endpoint: "http://localhost:7001",
balancer: "http://localhost:7001", // optional
uiService: "web" // (optional) read to the end for more info
};
Cluster.register("contracts", options);
var Contracts = new Meteor.Collection('contracts');
Meteor.methods({
addContract: addContract,
findContracts: findContracts
});
Meteor.publish("getContracts", function () {
return Contracts.find({});
});
function addContract(c){
var data = {
id: c.id,
type: c.type
};
Contracts.insert(data);
}
function findContracts(){
var contracts = Contracts.find().fetch();
return contracts;
}
I am accessing the methods from an angular controller in my UI app.
UI app server/app.js
Cluster.connect(mongodb://<username>:<passwd>#URL");
var options = {
endpoint: "http://localhost:8001",
balancer: "http://localhost:8001" // optional
//uiService: "web" // (optional) read to the end for more info
};
Cluster.register("web", options);
Cluster.allowPublicAccess("contracts");
UI app controller code
var contractConn = Cluster.discoverConnection('contracts');
contractConn.subscribe('getContracts');
var SubscribedContracts = new Mongo.Collection('SubscribedContracts', {connection: contractConn});
console.log('status', contractConn.status());
vm.contracts = SubscribedContracts.find({}).count();
contractConn.call("findContracts", function(err, result) {
if(err) {
throw err ;
}
else {
console.log(result);
}
});
This is what is happening:
* I can access methods on the contract server
* I can insert or find contracts using these methods
* My subscription is not working. fetch on the cursor shows 0 and count shows 0
* Status on the connection shows 'connecting'
What am I doing wrong with my subscription?
Sudi
I had to change the name of the client mongo collection to the same name as the name of the collection on the service.
I have a form that was created on it's own UI thread running in the system tray which I need to manipulate with a signalR connection from the server which I believe to be running on a background thread. I'm aware of the need to invoke controls when not accessing them from their UI thread. I am able to manipulate (make popup in my case) using the following code that is called on form load but would like a sanity check as I'm fairly new to async:
private void WireUpTransport()
{
// connect up to the signalR server
var connection = new HubConnection("http://localhost:32957/");
var messageHub = connection.CreateProxy("message");
var uiThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundTask = connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection: {0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("The connection was opened successfully");
}
});
// subscribe to the servers Broadcast method
messageHub.On<Domain.Message>("Broadcast", message =>
{
// do our work on the UI thread
var uiTask = backgroundTask.ContinueWith(t =>
{
popupNotifier.TitleText = message.Title + ", Priority: " + message.Priority.ToString();
popupNotifier.ContentText = message.Body;
popupNotifier.Popup();
}, uiThreadScheduler);
});
}
Does this look OK? It's working on my local machine but this has the potential to be rolled out on every user machine in our business and I need to get it right.
Technically you should hook up to all notifications (using On<T>) before you Start listening. As far as your async work I'm not quite sure what you were trying to do, but for some reason your chaining the notification to your UI in On<T> to the backgroundTask variable which is the Task that was returned to you by the call to Start. There's no reason for that to be involved there.
So this is probably what you want:
private void WireUpTransport()
{
// connect up to the signalR server
var connection = new HubConnection("http://localhost:32957/");
var messageHub = connection.CreateProxy("message");
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// subscribe to the servers Broadcast method
messageHub.On<Domain.Message>("Broadcast", message =>
{
// do our work on the UI thread
Task.Factory.StartNew(
() =>
{
popupNotifier.TitleText = message.Title + ", Priority: " + message.Priority.ToString();
popupNotifier.ContentText = message.Body;
popupNotifier.Popup();
},
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
});
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection: {0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("The connection was opened successfully");
}
});
}