Operation ID when using Application Insight - azure

I am providing my endpoint with a correlation ID:
I then read that ID from the HttpContext.Request.Headers and use that as my telemetry.Context.Operation.Id.
This works, but when I look in my log I have an extra entry which is auto generated by the framework. This entry has its own ID. How can I ensure the framework useses the same ID?
This is how I configure the service
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Users.Api.Services;
using Users.Api.Utility;
using Users.Services.Implementations;
using Users.Services.Interfaces;
using Users.Sql;
using Users.Utility;
using Packages.Api.Filters;
using Packages.Audit;
using Swashbuckle.AspNetCore.Swagger;
namespace Users.Api
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Dependency injection
services.AddAutoMapper();
services.AddDbContext<UsersContext>(
builder => builder.UseSqlServer(Environment.GetEnvironmentVariable("Connectionstring")));
services.AddScoped<IUserService, UserService>();
services.AddScoped<IIdentityService, IdentityService>();
services.AddScoped<IServiceBusCommunicator, ServiceBusCommunicator>();
services.AddScoped<IGraphClient, GraphClient>();
services.AddScoped<IClaimsHarvester, ClaimsHarvester>();
services.AddScoped<IUserRepository, UserRepository>();
services.AddSingleton<HttpClient>();
services.AddSingleton<EndpointConfiguration>();
services.AddSingleton<GraphConfiguration>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IAuditLogClient, AuditLogClient>();
var clientId = Environment.GetEnvironmentVariable("Authentication:AzureAd:ClientId");
var tenant = Environment.GetEnvironmentVariable("Authentication:AzureAd:Tenant");
var signInPolicyId = Environment.GetEnvironmentVariable("Authentication:AzureAd:SignInPolicyId");
var authority = $"https://login.microsoftonline.com/tfp/{tenant}/{signInPolicyId}/v2.0/";
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(bearerOptions =>
{
bearerOptions.Authority = authority;
bearerOptions.Audience = clientId;
bearerOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
};
});
services.AddMvc();
services.AddSwaggerGen(
options =>
{
options.SwaggerDoc("v1", new Info { Title = "Users API", Version = "v1" });
});
services.ConfigureSwaggerGen(options =>
{
options.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
options.OperationFilter<CorrelationHeaderParameterOperationFilter>();
options.OperationFilter<XTotalCountHeaderParameterOperationFilter>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
ContextInitializer contextInitializer)
{
if (env.IsDevelopment())
{
// loggerFactory.AddConsole(Configuration.GetSection("Logging"));
// loggerFactory.AddDebug();
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(
c =>
{
c.SwaggerEndpoint($"{Environment.GetEnvironmentVariable("ServiceFabric:UniqueUrlPath")}/swagger/v1/swagger.json", "Contacts API V1");
});
// Seed default values
contextInitializer.Seed();
}
private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationFailed: {arg.Exception.Message}";
arg.Response.ContentLength = s.Length;
arg.Response.Body.Write(Encoding.UTF8.GetBytes(s), 0, s.Length);
return Task.FromResult(0);
}
}
}

This is not generally supported by ApplicationInsights.
You may still achieve it, but have to write a custom request collection.
You'd need to remove RequestTelemetryTrackingModule from the DI container and add your custom middleware that tracks requests.
What will not work with this approach (code is below):
scenarios when you use different instrumentation keys on this service and upstream services. You can check out how it is handled in AppInsights SDK ( set requestTelemetry.Source and response header)
correlation with informational traces emitted by AspNetCore.
normally, request telemetry name contains route rather than path, you might need to figure it out
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry("ikey");
var requestModule =
services.FirstOrDefault(sd => sd.ImplementationType == typeof(RequestTrackingTelemetryModule));
if (requestModule != null)
{
services.Remove(requestModule);
}
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, TelemetryClient client)
{
app.UseMiddleware<RequestMiddleware>(client);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
public class RequestMiddleware
{
private readonly RequestDelegate next;
private readonly TelemetryClient telemetryClient;
public RequestMiddleware(
RequestDelegate next,
TelemetryClient telemetryClient)
{
this.telemetryClient = telemetryClient;
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var activity = new Activity("request");
if (context.Request.Headers.TryGetValue("x-my-correlation-id", out var val))
{
activity.SetParentId(val);
}
using (var request = telemetryClient.StartOperation<RequestTelemetry>(activity))
{
request.Telemetry.Url = context.Request.GetUri();
request.Telemetry.Context.Operation.Name = $"{context.Request.Method} {context.Request.Path.Value}";
request.Telemetry.Name = $"{context.Request.Method} {context.Request.Path.Value}";
try
{
await next.Invoke(context).ConfigureAwait(false);
}
catch (Exception e)
{
telemetryClient.TrackException(e);
request.Telemetry.Success = false;
throw;
}
finally
{
if (context.Response != null)
{
request.Telemetry.ResponseCode = context.Response.StatusCode.ToString();
request.Telemetry.Success = context.Response.StatusCode < 400;
}
else
{
request.Telemetry.Success = false;
}
}
}
}
}

Related

Azure Feature Flag is not updating after cache expiration

We have FeatureFlag: IsServiceNeeded with no label set in Feature Manager of Azure App Configuration. We have used in built method AddAzureAppConfiguration by setting up the cache interval.
We are using .net core 3.1 web api and Feature Manager of Azure App Configuration.
We had IsServiceNeeded enabled during initialization of the app and after few hours, we disabled IsServiceNeed. We wait for entire day, but don't see the difference since the below returns true instead of false. We were expecting it to update every 3 minutes due to how we have it configured in program.cs file.
await _featureManager.IsEnabledAsync("IsServiceNeeded")
Let me know if you see anything odd with below. Thanks in advance,
Here is the code snippet we are using it.
Program.cs file
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var configurationRoot = config.Build();
var appConfigConString = configurationRoot["AppConfigConnectionString"];
config.AddAzureAppConfiguration(options => options.Connect(appConfigConString).UseFeatureFlags(featureFlagOptions => {
**featureFlagOptions.CacheExpirationInterval = TimeSpan.FromMinutes(3);**
}));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs file
public class Startup
{
public IConfiguration Configuration { get; }
public string ContentRootPath { get; set; }
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
var conf = Configuration.GetSection("AppSettings").Get<Config>();
services.Configure<Config>(Configuration.GetSection("AppSettings"));
services.AddSingleton<IAppSettings>(c => conf);**
services.AddScoped<IProcessHandler, ProcessHandler>();
**services.AddFeatureManagement();**
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHsts();
app.UseHttpsRedirection();
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static void LoadMediatorHandlers(IServiceCollection services)
{
foreach (var assembly in Assembly
.GetEntryAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.Where(name => (name.FullName.Contains("Queries") || name.FullName.Contains("Commands"))))
{
services.AddMediatR(assembly);
}
services.AddMediatR(typeof(Startup));
services.AddScoped<IMediator, Mediator>();
}
}
Application of Feature Flag:
public class ProcessHandler : IProcessHandler
{
private readonly IFeatureManager _featureManager;
public ProcessHandler(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task<ClassA> ProcessXyz()
{
if (`await _featureManager.IsEnabledAsync("IsServiceNeeded")`)
{
return new ClassA();
}
return null;
}
}
Please Note: I have just added the required code and replaced actual names for security issues.
Thanks in advance
What is missing is the middleware that refreshes feature flags (and configuration) from Azure App Configuration.
Open your startup.cs, add below
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAzureAppConfiguration();
services.AddFeatureManagement();
}
And then add below
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseAzureAppConfiguration();
}

Unable to find WebHook filters for the 'xx' receiver. Add the required configuration by calling a receiver method that calls ''AddWebHooks'

I am implementing webhook using asp.net core 3.1 webhook package. This is a custom webhook poc and I need to expose this webhook to external users. During runtime I am facing below error and unable to solve it.
What can I try next?
Error:
Unable to find WebHook filters for the 'jr4o27tr2r472' receiver. Add the required configuration by calling a receiver-specific method that calls 'Microsoft.Extensions.DependencyInjection.IMvcBuilder.AddWebHooks' or 'IMvcCoreBuilder.AddWebHooks' in the application startup code. For example, call 'IMvcCoreBuilder.AddGitHubWebHooks' to configure a minimal GitHub receiver.
When I hit this url (http://localhost:49846/api/webhooks/incoming/jr4o27tr2r472/teleported), I am getting this issue in eventviewer.
Note: I have added required webhook services as part of configurationservice method.
public static class UnicornServiceCollectionSetup
{
public static void AddUnicornServices(IServiceCollection services)
{
WebHookMetadata.Register<UnicornMetadata>(services);
services.AddSingleton<UnicornSignatureFilter>();
}
}
public static class UnicornMvcCoreBuilderExtensions
{
public static IMvcCoreBuilder AddUnicornWebHooks(this IMvcCoreBuilder builder)
{
UnicornServiceCollectionSetup.AddUnicornServices(builder.Services);
return builder.AddWebHooks();
}
}
public class UnicornMetadata : WebHookMetadata, IWebHookFilterMetadata
{
private readonly UnicornSignatureFilter _verifySignatureFilter;
public UnicornMetadata(UnicornSignatureFilter verifySignatureFilter)
: base(UnicornConstants.ReceiverName)
{
_verifySignatureFilter = verifySignatureFilter;
}
public override WebHookBodyType BodyType => WebHookBodyType.Json;
public void AddFilters(WebHookFilterMetadataContext context)
{
context.Results.Add(_verifySignatureFilter);
}
}
public class UnicornSignatureFilter : WebHookVerifySignatureFilter,
IAsyncResourceFilter
{
private readonly byte[] _secret;
public UnicornSignatureFilter(//IOptions<UnicornConfig> options,
IConfiguration configuration,
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
: base(configuration, hostingEnvironment, loggerFactory)
{
//_secret = Encoding.UTF8.GetBytes(options.Value.SharedSecret);
_secret = Encoding.UTF8.GetBytes("secret");
}
public override string ReceiverName => UnicornConstants.ReceiverName;
public async Task OnResourceExecutionAsync(ResourceExecutingContext context,
ResourceExecutionDelegate next)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (next == null) throw new ArgumentNullException(nameof(next));
var request = context.HttpContext.Request;
if (!HttpMethods.IsPost(request.Method))
{
await next();
return;
}
var errorResult = EnsureSecureConnection(ReceiverName, request);
if (errorResult != null)
{
context.Result = errorResult;
return;
}
var header = GetRequestHeader(request,
UnicornConstants.SignatureHeaderName,
out errorResult);
if (errorResult != null)
{
context.Result = errorResult;
return;
}
byte[] payload;
using (var ms = new MemoryStream())
{
HttpRequestRewindExtensions.EnableBuffering(request);
await request.Body.CopyToAsync(ms);
payload = ms.ToArray();
request.Body.Position = 0;
}
if (payload == null || payload.Length == 0)
{
context.Result = new BadRequestObjectResult("No payload");
return;
}
var digest = FromBase64(header, UnicornConstants.SignatureHeaderName);
var secretPlusJson = _secret.Concat(payload).ToArray();
using (var sha512 = new SHA512Managed())
{
if (!SecretEqual(sha512.ComputeHash(secretPlusJson), digest))
{
context.Result =
new BadRequestObjectResult("Signature verification failed");
return;
}
}
await next();
}
}
Note: I am attaching source code in this webhookpoc.

How to use dependency inject for TelemetryConfiguration in Azure Function

I try to use Dependency injection in Azure Functions for TelemetryConfiguration. In my function I will have it resolved when I inject TelemetryConfiguration in the functions constructor. I suppose I don't really understand how I will do with TelemetryConfiguration in StartUp, thats why I get an exception. How will I add the TelemetryConfiguration I already configured.
I have did an easy example here what I'm doing so far.
[assembly: FunctionsStartup(typeof(StartUp))]
public class StartUp : FunctionsStartup
{
private string OmsModule { get; } = "OMS.VA";
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.Configure<TelemetryConfiguration>(
(o) =>
{
o.InstrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
o.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
});
}
}
public class StopPlaceUpdateTimerTrigger
{
private TelemetryClient _telemetryClient;
private string _azureWebJobsStorage;
public StopPlaceUpdateTimerTrigger(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName("StopPlaceLoader")]
public async Task StopPlaceLoaderMain([TimerTrigger("%CRON_EXPRESSION%", RunOnStartup = true)]TimerInfo myTimerInfo, ILogger log, ExecutionContext context)
{
SetConfig(context);
var cloudTable = await GetCloudTableAsync();
if (cloudTable == null)
{
//Do nothing
}
//Do nothing
}
private async Task<CloudTable> GetCloudTableAsync()
{
var storageAccount = CloudStorageAccount.Parse(_azureWebJobsStorage);
var tableClient = storageAccount.CreateCloudTableClient();
var table = tableClient.GetTableReference(nameof(StopPlaceLoaderCacheRecord));
if (!await table.ExistsAsync())
{
await table.CreateIfNotExistsAsync();
}
return table;
}
private void SetConfig(ExecutionContext context)
{
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true)
.AddEnvironmentVariables()
.Build();
_azureWebJobsStorage = config["AzureWebJobsStorage"];
}
}
//local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol...",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"EnableMSDeployAppOffline": "True",
"CRON_EXPRESSION": "0 */5 22-3 * * *",
"APPINSIGHTS_INSTRUMENTATIONKEY": "..."
}
}
I get the following Exception;
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration' while attempting to activate 'OMS.VA.RealTime.StopPlaceLoader.StopPlaceUpdateTimerTrigger'.
Update:
We can change this line of code var newConfig = TelemetryConfiguration.Active; to var newConfig = TelemetryConfiguration.CreateDefault(); , since TelemetryConfiguration.Active is deprecated.
Please use the code below for TelemetryConfiguration DI, I test it with a blob trigger function and works well:
using System.IO;
using System.Linq;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
[assembly: WebJobsStartup(typeof(FunctionApp17.MyStartup))]
namespace FunctionApp17
{
public class MyStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var configDescriptor = builder.Services.SingleOrDefault(tc => tc.ServiceType == typeof(TelemetryConfiguration));
if (configDescriptor?.ImplementationFactory != null)
{
var implFactory = configDescriptor.ImplementationFactory;
builder.Services.Remove(configDescriptor);
builder.Services.AddSingleton(provider =>
{
if (implFactory.Invoke(provider) is TelemetryConfiguration config)
{
var newConfig = TelemetryConfiguration.Active;
newConfig.ApplicationIdProvider = config.ApplicationIdProvider;
newConfig.InstrumentationKey = config.InstrumentationKey;
return newConfig;
}
return null;
});
}
}
}
public class Function1
{
private TelemetryClient _telemetryClient;
public Function1(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName("Function1")]
public void Run([BlobTrigger("samples-workitems/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"!!!!!!!!!! C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
_telemetryClient.TrackTrace("this is a test message from DI of telemetry client !!!!!!!!!!!!!!");
}
}
}
the test result as below, I can see the logs in the application insights in azure portal:
And one more thing, I see you try to use ITelemetry Initializer in your code. You can follow this GitHub issue for your ITelemetry Initializer or Itelemetry Processor
If you use builder.Services.Configure<TelemetryConfiguration>() to configure, you are using Options pattern in ASP.NET Core.
To access the option, you need to do as following:
public StopPlaceUpdateTimerTrigger(IOptionsMonitor<TelemetryConfiguration> telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration.CurrentValue);
}
If you just want to directly use TelemetryConfiguration object, you need to add it in service collection:
builder.Services.AddSingleton<TelemetryConfiguration >(sp =>
{
var telemetryConfiguration = new TelemetryConfiguration();
telemetryConfiguration.InstrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
telemetryConfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
return telemetryConfiguration;
}
Then you can :
public StopPlaceUpdateTimerTrigger(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
Hope it helps.

Azure Notification Hub - Android app crashing when receive notification

I'm using Azure Notification Hub with Firebase Cloud Messaging and Xamarin. When I have the app in the background or in use, I can receive notifications. But when I kill the app from the Android current app list, I'm able to receive one notification but after that, I have the error "my_app stopped (close the application)". Any idea why ? I don't have error log because it happens when the app is killed.
This is my code, from the documentation
using System;
using System.Collections.Generic;
using System.Text;
using Android.App;
using Android.Content;
using Android.Util;
using WindowsAzure.Messaging;
using Gcm.Client;
[assembly: Permission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "#PACKAGE_NAME#.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name ="com.google.android.c2dm.permission.RECEIVE")]
//GET_ACCOUNTS is needed only for Android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
namespace Waka.Droid
{
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK },
Categories = new string[] { "#PACKAGE_NAME#" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY },
Categories = new string[] { "#PACKAGE_NAME#" })]
public class MyBroadcastReceiver : GcmBroadcastReceiverBase<PushHandlerService>
{
public static string[] SENDER_IDS = new string[] { Constants.SenderID };
public const string TAG = "MyBroadcastReceiver-GCM";
}
[Service] // Must use the service tag
public class PushHandlerService : GcmServiceBase
{
public static string RegistrationID { get; private set; }
private NotificationHub Hub { get; set; }
public PushHandlerService() : base(Constants.SenderID)
{
}
protected override void OnRegistered(Context context, string registrationId)
{
RegistrationID = registrationId;
Hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString,
context);
try
{
Hub.UnregisterAll(registrationId);
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
//var tags = new List<string>() { "falcons" }; // create tags if you want
var tags = new List<string>() { };
try
{
var hubRegistration = Hub.Register(registrationId, tags.ToArray());
}
catch (Exception ex)
{
Log.Error(MyBroadcastReceiver.TAG, ex.Message);
}
}
protected override void OnMessage(Context context, Intent intent)
{
var msg = new StringBuilder();
if (intent != null && intent.Extras != null)
{
foreach (var key in intent.Extras.KeySet())
msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
}
string messageText = intent.Extras.GetString("message");
if (!string.IsNullOrEmpty(messageText))
{
createNotification("Shotgun!", messageText);
}
else
{
createNotification("Undefined", msg.ToString());
}
}
void createNotification(string title, string desc)
{
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
//Create an intent to show UI
var uiIntent = new Intent(this, typeof(MainActivity));
PendingIntent resultPendingIntent =
PendingIntent.GetActivity(
this,
0,
uiIntent,
0
);
Notification noti = new Notification.Builder(this)
.SetContentTitle("" + title)
.SetContentText(desc)
.SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
.SetContentIntent(resultPendingIntent)
.SetDefaults(NotificationDefaults.All)
.Build();
//Show the notification
notificationManager.Notify(1, noti);
dialogNotify(title, desc);
}
protected void dialogNotify(String title, String message)
{
MainActivity.instance.RunOnUiThread(() =>
{
AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.instance);
AlertDialog alert = dlg.Create();
alert.SetTitle(title);
alert.SetButton("Ok", delegate
{
alert.Dismiss();
});
alert.SetMessage(message);
alert.Show();
});
}
protected override void OnUnRegistered(Context context, string registrationId)
{
}
protected override bool OnRecoverableError(Context context, string errorId)
{
return base.OnRecoverableError(context, errorId);
}
protected override void OnError(Context context, string errorId)
{
}
}
}
MainActivity.cs:
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Util;
using Gcm.Client;
namespace Waka.Droid
{
[Activity(Label = "Waka.Droid", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity instance;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
instance = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
RegisterWithGCM();
}
private void RegisterWithGCM()
{
// Check to ensure everything's set up right
GcmClient.CheckDevice(this);
GcmClient.CheckManifest(this);
// Register for push notifications
GcmClient.Register(this, Constants.SenderID);
}
}
}

Service Stack Razor View Not Found

I added the RazorPlugin along with a Test.cshtml in the root View folder (tried wwwroot as well along with TestGet.cshtml). This is an donet Core project. Whenever browsing to: /api/test the following error is generated:
Snapshot of TestGet generated by ServiceStack on 6/3/2017 4:20:40 PM
view json datasource from original url:
http://localhost:51550/api/test? in other formats: json xml csv jsv
Response Status Error CodeNullReferenceExceptionMessageObject
reference not set to an instance of an object.Stack Trace[TestGet:
6/3/2017 4:20:40 PM]: [REQUEST: {}] System.NullReferenceException:
Object reference not set to an instance of an object. at
ServiceStack.Mvc.RazorFormat.FindView(IEnumerable1 viewNames) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line
174 at ServiceStack.Mvc.RazorFormat.ProcessRequest(IRequest req,
IResponse res, Object dto) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line
138 at System.Linq.Enumerable.Any[TSource](IEnumerable1 source,
Func`2 predicate) at
ServiceStack.Formats.HtmlFormat.SerializeToStream(IRequest req, Object
response, IResponse res) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack/Formats/HtmlFormat.cs:line
63Errors
The other formats work (json/etc).
public class Test
{
public string ExternalId { get; set; }
}
[Authenticate, Route("/test")]
public class TestGet : IGet, IReturn<Test>
{
}
public class TestService : Service
{
public IAutoQueryDb _autoQueryDb { get; set; }
public IDbConnectionFactory _dbFactory { get; set; }
public TestService(IDbConnectionFactory dbFactory)
{
_dbFactory = dbFactory;
}
public Test Get(TestGet request)
{
var test = new Test();
test.ExternalId = "abc";
return test;
}
}
Test.cshtml
#using App.Shared.DomainModel
#model Test
<div>Test</div>
AppHost.cs
using App.DomainServices;
using App.FontEnd.Infrastructure.Configuration;
using App.FontEnd.Infrastructure.Core;
using App.Shared.DomainModel;
using Funq;
using LightInject;
using ServiceStack;
using ServiceStack.Admin;
using ServiceStack.Api.Swagger;
using ServiceStack.Auth;
using ServiceStack.Caching;
using ServiceStack.Configuration;
using ServiceStack.Data;
using ServiceStack.Mvc;
using ServiceStack.OrmLite;
using ServiceStack.Validation;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace App.FrontEnd
{
public class AppHost : AppHostBase
{
/// <summary>
/// Base constructor requires a Name and Assembly where web service implementation is located
/// </summary>
public AppHost()
: base("TestApi", typeof(CompanyService).GetAssembly())
{
}
/// <summary>
/// Application specific configuration
/// This method should initialize any IoC resources utilized by your web service classes.
/// </summary>
public override void Configure(Container container)
{
this.GlobalRequestFilters.Add((httpReq, httpResp, requestDto) =>
{
var currentSession = httpReq.GetSession();
if (currentSession != null)
{
RequestContext.Instance.Items.Add("CurrentUserName", currentSession.UserName);
RequestContext.Instance.Items.Add("CurrentUserId", currentSession.UserAuthId);
}
});
ServiceContainer LightContainer = new ServiceContainer();
LightContainer.RegisterInstance<IDbConnectionFactory>
(
new OrmLiteConnectionFactory(
"removed",
SqlServer2014Dialect.Provider
)
);
LightContainer.Register<IDbConnection>(c =>
c.GetInstance<IDbConnectionFactory>().OpenDbConnection(),
new PerScopeLifetime()
);
LightContainer.Register<OrmLiteAppSettings>(c =>
new OrmLiteAppSettings(c.GetInstance<IDbConnectionFactory>()));
LightContainer.Register<ServiceStack.Web.IServiceGatewayFactory>(x => new ApiServiceGatewayFactory());
container.Adapter = new LightInjectAdapter(LightContainer);
var settings = LightContainer.GetInstance<OrmLiteAppSettings>();
settings.InitSchema();
AppSettings = new MultiAppSettings(
settings
);
container.Register<ICacheClient>(new OrmLiteCacheClient
{
DbFactory = LightContainer.GetInstance<IDbConnectionFactory>()
});
var cacheclient = container.Resolve<ICacheClient>();
cacheclient.InitSchema();
AuthConfig(container, AppSettings);
Plugins.Add(new RegistrationFeature());
Plugins.Add(new SwaggerFeature());
Plugins.Add(new RequestLogsFeature());
Plugins.Add(new PostmanFeature());
Plugins.Add(new CorsFeature(allowCredentials: true));
Plugins.Add(new ValidationFeature());
Plugins.Add(new RazorFormat());
OrmLiteConfig.InsertFilter = (dbCmd, row) =>
{
var auditRow = row as CoreModel;
if (auditRow != null)
{
var currentDate = DateTime.UtcNow;
var insertUserId = RequestContext.Instance.Items["CurrentUserId"] as string;
auditRow.Id = Guid.NewGuid();
auditRow.CreatedDate = currentDate;
auditRow.CreatedBy = insertUserId;
auditRow.UpdatedDate = currentDate;
auditRow.UpdatedBy = insertUserId;
}
};
OrmLiteConfig.UpdateFilter = (dbCmd, row) =>
{
var auditRow = row as CoreModel;
if (auditRow != null)
{
var updateUserId = RequestContext.Instance.Items["CurrentUserId"] as string;
auditRow.UpdatedDate = DateTime.UtcNow;
auditRow.UpdatedBy = updateUserId;
}
};
var aq = new AutoQueryFeature { MaxLimit = 100, EnableAutoQueryViewer = true };
aq.ImplicitConventions.Add("%neq", aq.ImplicitConventions["%NotEqualTo"]);
aq.ImplicitConventions.Add("%eq", "{Field} = {Value}");
Plugins.Add(aq);
Plugins.Add(new AdminFeature());
SetConfig(new HostConfig
{
HandlerFactoryPath = "api",
DebugMode = true
});
container.CheckAdapterFirst = true;
//Set up service stack validators
container.ValidatorsSetup();
}
public void AuthConfig(Container container, IAppSettings settings)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(AppSettings),
new JwtAuthProvider(AppSettings)
{
AuthKeyBase64 = "abcdefgh"
},
new BasicAuthProvider()
}));
var authRepo = CreateOrmLiteAuthRepo(container, settings);
}
private static IUserAuthRepository CreateOrmLiteAuthRepo(Container container, IAppSettings appSettings)
{
//Store User Data into the referenced SqlServer database
container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
//Use OrmLite DB Connection to persist the UserAuth and AuthProvider info
var authRepo = (OrmLiteAuthRepository)container.Resolve<IAuthRepository>();
//If using and RDBMS to persist UserAuth, we must create required tables
if (appSettings.Get("RecreateAuthTables", false))
authRepo.DropAndReCreateTables(); //Drop and re-create all Auth and registration tables
else
authRepo.InitSchema(); //Create only the missing tables
return authRepo;
}
}
}
I've created a minimal verifiable MVC test project to simulate your configuration which is working as expected at: https://github.com/NetCoreApps/scratch/tree/master/src/RazorApi
It includes the same Test Services, (sans [Authenticate] attribute):
public class Test
{
public string ExternalId { get; set; }
}
[Route("/test")]
public class TestGet : IGet, IReturn<Test>
{
}
public class TestService : Service
{
public Test Get(TestGet request)
{
var test = new Test { ExternalId = "abc" };
return test;
}
}
And the minimal AppHost configuration which just sets the HandlerFactoryPath to api and registers ServiceStack's RazorFormat plugin:
public class AppHost : AppHostBase
{
public AppHost()
: base("ServiceStack + .NET Core", typeof(MyServices).GetTypeInfo().Assembly) {}
public override void Configure(Funq.Container container)
{
SetConfig(new HostConfig
{
HandlerFactoryPath = "api",
DebugMode = true,
});
Plugins.Add(new RazorFormat());
}
}
With the Test.cshtml maintained in /Views/Test.cshtml:
#model Test
#{
Layout = "_Layout";
}
<h1>Test.cshtml</h1>
<p>#Model.ExternalId</p>
Which works as expected with the Razor View executed when calling:
http://localhost:5000/api/test
Which also works when renamed to match the Request DTO at TestGet.cshtml.
As the issue seems specific to your project I'd compare your layout with bare RazorApi Github Project to see if you can find any differences, failing that I'd recommend commenting out configuration to get it to a working state then uncomment sections at a time to find out which configuration is causing the issue.

Resources