Base solution for your next web application

Activities of "devinedon"

Hey,

Similar outcome. Is the handshaking logic similar? Surely there is more to all this?

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?

Angular .NET Core 9.3

Looks like I cant connect to Azure Signal R via the web host. Still trying to connect to localhost?

Is there anything you think is missing? I even tried removing MapHub via UseEndpoints and moved them to UseAzureSignalR (not that I should need to do this?)

Thanks!

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')

Showing 11 to 20 of 46 entries