Thanks for the response @rickfrankel
I updated but sadly didnt work
Here is the startup file
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.AspNetZeroCore.Web.Authentication.JwtBearer;
using Abp.Castle.Logging.Log4Net;
using Abp.Extensions;
using Abp.Hangfire;
using Abp.PlugIns;
using Abp.Timing;
using Castle.Facilities.Logging;
using GraphQL.Server;
using GraphQL.Server.Ui.Playground;
using Hangfire;
using HealthChecks.UI.Client;
using IdentityServer4.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.SignalR;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Owl.reCAPTCHA;
using Stripe;
using SynapWare.Authorization;
using SynapWare.BlobStorage;
using SynapWare.Configuration;
using SynapWare.Configure;
using SynapWare.CosmosDB;
using SynapWare.EntityFrameworkCore;
using SynapWare.Identity;
using SynapWare.Schemas;
using SynapWare.Web.Chat.SignalR;
using SynapWare.Web.Common;
using SynapWare.Web.HealthCheck;
using SynapWare.Web.IdentityServer;
using SynapWare.Web.Swagger;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using HealthChecksUISettings = HealthChecks.UI.Configuration.Settings;
using ILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory;
namespace SynapWare.Web.Startup
{
public class Startup
{
private const string DefaultCorsPolicyName = "localhost";
private readonly IConfigurationRoot _appConfiguration;
private readonly IWebHostEnvironment _hostingEnvironment;
public Startup(IWebHostEnvironment env)
{
_hostingEnvironment = env;
_appConfiguration = env.GetAppConfiguration();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//set time zone
Clock.Provider = ClockProviders.Utc;
// Add CosmosDb. This verifies database and collections existence.
ConfigureCosmosDB(services);
//MVC
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
}).AddNewtonsoftJson();
services
.AddSignalR(options => { options.EnableDetailedErrors = true; })
.AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
{
new ServiceEndpoint(_appConfiguration["Azure:SignalR:PrimaryConnectionString"], EndpointType.Primary, "Name1"),
new ServiceEndpoint(_appConfiguration["Azure:SignalR:SecondaryConnectionString"], EndpointType.Secondary, "Name2"),
});
//Configure CORS for angular2 UI
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
//App:CorsOrigins in appsettings.json can contain more than one address with splitted by comma.
builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
if (bool.Parse(_appConfiguration["KestrelServer:IsEnabled"]))
{
ConfigureKestrel(services);
}
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
//Identity server
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
IdentityServerRegistrar.Register(services, _appConfiguration, options =>
options.UserInteraction = new UserInteractionOptions()
{
LoginUrl = "/UI/Login",
LogoutUrl = "/UI/LogOut",
ErrorUrl = "/Error"
});
}
if (WebConsts.SwaggerUiEnabled)
{
//Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo() { Title = "SynapWare API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.ParameterFilter<SwaggerEnumParameterFilter>();
options.SchemaFilter<SwaggerEnumSchemaFilter>();
options.OperationFilter<SwaggerOperationIdFilter>();
options.OperationFilter<SwaggerOperationFilter>();
options.CustomDefaultSchemaIdSelector();
}).AddSwaggerGenNewtonsoftSupport();
}
//Recaptcha
services.AddreCAPTCHAV3(x =>
{
x.SiteKey = _appConfiguration["Recaptcha:SiteKey"];
x.SiteSecret = _appConfiguration["Recaptcha:SecretKey"];
});
if (WebConsts.HangfireDashboardEnabled)
{
//Hangfire(Enable to use Hangfire instead of default job manager)
services.AddHangfire(config =>
{
config.UseSqlServerStorage(_appConfiguration.GetConnectionString("Default"));
});
}
if (WebConsts.GraphQL.Enabled)
{
services.AddAndConfigureGraphQL();
}
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
services.AddAbpZeroHealthCheck();
var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");
if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
{
services.Configure<HealthChecksUISettings>(settings =>
{
healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
});
services.AddHealthChecksUI();
}
}
services.AddScoped<IDeviceDataRepository, DeviceDataRepository>();
services.AddScoped<IBlobRepository>(factory =>
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_appConfiguration["ConnectionStrings:DeviceBlobStorage"]);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(_appConfiguration["BlobStorage:DeviceContainerName"]);
return new AzureBlobRepository(container);
});
//Configure Abp and Dependency Injection
return services.AddAbp<SynapWareWebHostModule>(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig(_hostingEnvironment.IsDevelopment()
? "log4net.config"
: "log4net.Production.config")
);
options.PlugInSources.AddFolder(Path.Combine(_hostingEnvironment.WebRootPath, "Plugins"), SearchOption.AllDirectories);
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
//Initializes ABP framework.
app.UseAbp(options =>
{
options.UseAbpRequestLocalization = false; //used below: UseAbpRequestLocalization
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseStatusCodePagesWithRedirects("~/Error?statusCode={0}");
app.UseExceptionHandler("/Error");
}
app.Use(async (context, next) => { await next(); if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) { context.Request.Path = "/index.html"; await next(); } });
app.UseStaticFiles();
app.UseRouting();
app.UseCors(DefaultCorsPolicyName); //Enable CORS!
app.UseAuthentication();
app.UseJwtTokenMiddleware();
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
app.UseJwtTokenMiddleware("IdentityBearer");
app.UseIdentityServer();
}
app.UseAuthorization();
using (var scope = app.ApplicationServices.CreateScope())
{
if (scope.ServiceProvider.GetService<DatabaseCheckHelper>().Exist(_appConfiguration["ConnectionStrings:Default"]))
{
app.UseAbpRequestLocalization();
}
}
if (WebConsts.HangfireDashboardEnabled)
{
//Hangfire dashboard &server(Enable to use Hangfire instead of default job manager)
app.UseHangfireDashboard(WebConsts.HangfireDashboardEndPoint, new DashboardOptions
{
Authorization = new[] { new AbpHangfireAuthorizationFilter(AppPermissions.Pages_Administration_HangfireDashboard) }
});
app.UseHangfireServer();
}
if (bool.Parse(_appConfiguration["Payment:Stripe:IsActive"]))
{
StripeConfiguration.ApiKey = _appConfiguration["Payment:Stripe:SecretKey"];
}
if (WebConsts.GraphQL.Enabled)
{
app.UseGraphQL<MainSchema>();
if (WebConsts.GraphQL.PlaygroundEnabled)
{
app.UseGraphQLPlayground(
new GraphQLPlaygroundOptions()); //to explorer API navigate https://*DOMAIN*/ui/playground
}
}
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
endpoints.MapHub<ChatHub>(AppConsts.SignalRChatHubPath);
endpoints.MapHub<TestChatHub>("/chat");
endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
}
});
//app.UseAzureSignalR(endpoints =>
//{
// endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
// endpoints.MapHub<ChatHub>(AppConsts.SignalRChatHubPath);
// endpoints.MapHub<ChatHub>("/chat");
//});
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
{
app.UseHealthChecksUI();
}
}
if (WebConsts.SwaggerUiEnabled)
{
// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger();
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint(_appConfiguration["App:SwaggerEndPoint"], "SynapWare API V1");
options.IndexStream = () => Assembly.GetExecutingAssembly()
.GetManifestResourceStream("SynapWare.Web.wwwroot.swagger.ui.index.html");
options.InjectBaseUrl(_appConfiguration["App:ServerRootAddress"]);
}); //URL: /swagger
}
}
private void ConfigureKestrel(IServiceCollection services)
{
services.Configure<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions>(options =>
{
options.Listen(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 443),
listenOptions =>
{
var certPassword = _appConfiguration.GetValue<string>("Kestrel:Certificates:Default:Password");
var certPath = _appConfiguration.GetValue<string>("Kestrel:Certificates:Default:Path");
var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(certPath, certPassword);
listenOptions.UseHttps(new HttpsConnectionAdapterOptions()
{
ServerCertificate = cert
});
});
});
}
private void ConfigureCosmosDB(IServiceCollection services)
{
//https://github.com/Azure-Samples/PartitionedRepository
//To connect to multiple collections see example above
var serviceEndpoint = new Uri(_appConfiguration.GetValue<string>("CosmosDb:ServiceEndpoint"), UriKind.Absolute);
var authKey = _appConfiguration.GetValue<string>("CosmosDb:authKey");
var databaseName = _appConfiguration.GetValue<string>("CosmosDb:DatabaseName");
var collectionNames = new List<string>
{
_appConfiguration.GetValue<string>("CosmosDb:DeviceDataCollectionName"),
_appConfiguration.GetValue<string>("CosmosDb:LatestDeviceDataCollectionName")
};
var documentClient = new DocumentClient(serviceEndpoint, authKey, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
documentClient.OpenAsync().Wait();
var cosmosDbClientFactory = new CosmosDbClientFactory(databaseName, collectionNames, documentClient);
cosmosDbClientFactory.EnsureDbSetupAsync().Wait();
services.AddSingleton<ICosmosDbClientFactory>(cosmosDbClientFactory);
}
}
}
So, good news! It works if go back to default loginview.xaml. Must be something to do with my styling. Will report back asap
EDIT : Syncfusion related. Only does this on a physical iOS device, Android and iOS emulator works 100%. Working - License related :/ Sorry about that
Thanks @ismcagdas,
I dont make use of DataTemplates on the login page.
Setup iOS dev on my new iMac and same error; only an issue on a physical device (iPhone 11 pro, 14.3 if it matters at all)
My LoginView.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="LoginPage"
xmlns:base="clr-namespace:SynapWare.ViewModels.Base;assembly=SynapWare.Mobile.Shared"
xmlns:extensions="clr-namespace:SynapWare.Extensions.MarkupExtensions;assembly=SynapWare.Mobile.Shared"
xmlns:behaviors="clr-namespace:SynapWare.Behaviors;assembly=SynapWare.Mobile.Shared"
xmlns:controls="clr-namespace:SynapWare.Controls;assembly=SynapWare.Mobile.Shared"
xmlns:buttons="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
x:Class="SynapWare.Views.LoginView"
BackgroundColor="{StaticResource LoginBackgroundColor}"
base:ViewManager.AutoWireViewModel="true"
NavigationPage.HasNavigationBar="False"
Title="{extensions:Translate LogIn}">
<ContentPage.ToolbarItems>
<controls:HideableToolbarItem
Order="Primary"
Text="{extensions:Translate ChangeTenant}"
Command="{Binding ChangeTenantCommand}"
IsVisible="{Binding IsMultiTenancyEnabled}"
ParentPage="{x:Reference LoginPage}" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<ScrollView>
<Grid RowDefinitions="0.10*, 150, auto, auto, 0.20*" ColumnDefinitions="*, 18*, *"
RowSpacing="0"
ColumnSpacing="0">
<Image Grid.Row="1"
Grid.Column="1"
Aspect="AspectFit"
Source="{extensions:ImageSource 'SynapWare.UI.Assets.Images.Synap_Black_Teal.png'}">
</Image>
<!--<Frame Grid.Row="2"
Grid.Column="1"
IsVisible="{Binding IsMultiTenancyEnabled}"
Style="{StaticResource RoundedFrame}">
<Grid Padding="0" ColumnSpacing="5" RowSpacing="0" HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
LineBreakMode="WordWrap"
Text="{extensions:Translate CurrentTenant}" />
<Label Grid.Row="0"
Grid.Column="1"
LineBreakMode="WordWrap"
Text="{Binding CurrentTenancyNameOrDefault}"
Style="{StaticResource ActiveLabel}"/>
</Grid>
</Frame>-->
<Frame Grid.Row="3" BackgroundColor="White" BorderColor="White"
Grid.Column="1"
Style="{StaticResource RoundedFrame}">
<StackLayout VerticalOptions="StartAndExpand" Spacing="0">
<Label Text="{extensions:Translate UserNameOrEmail}" TextColor="{StaticResource PrimaryColor}" />
<Entry Text="{Binding UserName, Mode=TwoWay}"
Keyboard="{StaticResource NoCapitalizationKeyboard}"
x:Name="UsernameEntry"/>
<Label Text="{extensions:Translate Password}" Margin="0,10,0,0" TextColor="{StaticResource PrimaryColor}" />
<Entry IsPassword="True"
Text="{Binding Password, Mode=TwoWay}"
Keyboard="{StaticResource NoCapitalizationKeyboard}"
x:Name="PasswordEntry"/>
<Label Text="{extensions:Translate ForgotPassword}"
FontSize="Micro"
HorizontalOptions="EndAndExpand"
TextColor="{StaticResource PrimaryColor}"
Margin="0,10,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ForgotPasswordCommand}" />
</Label.GestureRecognizers>
</Label>
<buttons:SfButton Text="{extensions:Translate LogIn}" x:Name="LoginButton" Margin="0,20,0,0"
IsEnabled="{Binding IsLoginEnabled}"
Style="{StaticResource ActionButton}"
Command="{Binding LoginUserCommand}"
CornerRadius="25"/>
<!--<Label Text="{extensions:Translate EmailActivation}"
HorizontalOptions="StartAndExpand"
TextColor="{StaticResource PrimaryColor}"
Margin="0,10,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EmailActivationCommand}" />
</Label.GestureRecognizers>
</Label>-->
<!--<Picker Title="{extensions:Translate Languages}"
Margin="0,10,0,0"
ItemsSource="{Binding Languages}"
ItemDisplayBinding="{Binding DisplayName}"
SelectedItem="{Binding SelectedLanguage}"/>-->
</StackLayout>
</Frame>
</Grid>
</ScrollView>
</ContentPage.Content>
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:ActionCollection>
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:ActionCollection>
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
</ContentPage>
Hey @rickfrankel,
For some reason I'm still communicating locally and its not using Azure Signal R's service? https://support.aspnetzero.com/QA/Questions/9976/Replace-Signal-R-with-Azure-Signal-R
Do you have any idea why this is happening?
https://support.aspnetzero.com/QA/Questions/9493/SignalR-404-on-Azure-Multiple-App-Servers#answer-adf4666b-0f04-9740-3a44-39f7119bbbde
This is why I'm confused, its the same as this....
Hey,
What config are you talking about?
services.AddSignalR().AddAzureSignalR(_appConfiguration.GetValue<string>("Azure:SignalR:ConnectionString"));
My setup is now setup like this
"Azure": {
"SignalR": {
"ConnectionString": "Endpoint=https://synap-staging-signalr-service.service.signalr.net;AccessKey=abcdef=;Version=1.0;"
}
},
However its still using localhost?
Even adding the exact Azure Signal R Service sample code (const connection) shows localhost too (via the console.log)
Any idea why its using localhost rather than the actual Azure service? I thought it was automatic?
So making progress - web host is changed to use Azure Signal R and all looks good
However, how would I access IApplicationbuilder in Azure Functions in order to map the endpoint (if you know of course). Could I get it via AbpModule? Just code below showing how you guys do it in an ASPNET Core application
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
}
A host error has occurred during startup operation '39caf9b3-b3fa-472c-a351-6cc74d272ece'. [2020-12-14T11:48:02.356Z] func: Invalid host services. Microsoft.Azure.WebJobs.Script.WebHost: The following service registrations did not match the expected services: [2020-12-14T11:48:02.358Z] [Invalid] ServiceType: Microsoft.Extensions.Hosting.IHostedService, Lifetime: Singleton, ImplementationType: Microsoft.Azure.SignalR.HeartBeat, Microsoft.Azure.SignalR, Version=1.6.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60. Value cannot be null. (Parameter 'provider')