C# Get expiry timestamp from JWT token

JWT tokens (or Json Web Tokens) are an open-standard the defines a way to transmit information between 2 parties in a secure manner. Identity Server 4 uses JWT as a security token.

These tokens have an expiry timestamp, and if you handle the tokens yourself, you need to read the token expiry and refresh the token if the token is expired.

Microsoft have made a brilliant library, System.IdentityModel.Tokens.Jwt to handle JWT tokens, but the package does also have a lot of dependencies that were incompatible with my application, so I chose to use JWT.Net instead, as this package does not have any dependencies at all.

THE ANATOMY OF A JWT TOKEN:

Json Web Token Anatomy

Json Web Token Anatomy

A JWT token consists of a header, a payload and a signature. It is in the payload that you find the expiry timestamp in the “exp” field. The timestamp is the stupid UNIX timestamp format, but fear not, .NET knows how to convert the timestamp to a real DateTime.

STEP 1: CREATE A PAYLOAD MODEL CLASS

JWT.Net is not as powerful as System.IdentityModel.Tokens.Jwt, so you need to create a model class of the payload section. The class, however, is very simple:

namespace MyCode
{
  public class JwtToken
  {
    public long exp { get; set; }
  }
}

STEP2: USE JWT.Net TO GET THE EXPIRY FROM THE TOKEN PAYLOAD

Final step is to take the JWT Token string and decode it to the JwtToken class, then convert the UNIX timestamp to a local time:

using System;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;

namespace MyCode
{
  public class JWTService
  {
    private IJsonSerializer _serializer = new JsonNetSerializer();
    private IDateTimeProvider _provider = new UtcDateTimeProvider();
    private IBase64UrlEncoder _urlEncoder = new JwtBase64UrlEncoder();
    private IJwtAlgorithm _algorithm = new HMACSHA256Algorithm();

    public DateTime GetExpiryTimestamp(string accessToken)
    {
      try
      {
        IJwtValidator _validator = new JwtValidator(_serializer, _provider);
        IJwtDecoder decoder = new JwtDecoder(_serializer, _validator, _urlEncoder, _algorithm);
        var token = decoder.DecodeToObject<JwtToken>(accessToken);
        DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(token.exp);
        return dateTimeOffset.LocalDateTime;
      }
      catch (TokenExpiredException)
      {
        return DateTime.MinValue;
      }
      catch (SignatureVerificationException)
      {
        return DateTime.MinValue;
      }
      catch (Exception ex)
      {
        // ... remember to handle the generic exception ...
        return DateTime.MinValue;
      }
    }
  }
}

That’s it. You are now a security expert. Happy coding.

FUNNY FINAL NOTE:

The term “JWT Token” is a redundant acronym syndrome, or RAS-syndrome. It is the use of the last word of the acronym in conjunction with the abbreviated form. It’s like saying “PIN number” or “PDF format”. In reality, when saying “JWT Token”, you are really saying “json web token token” :).

MORE TO READ:

Posted in .net, .NET Core, c#, General .NET, Microsoft Azure | Tagged , , , , | Leave a comment

Application Insights not working in .NET Core Console Application – Remember to flush the TelemetryClient

My .NET Core console application did not write to my Application Insights instance. Well, it did, but only sometimes.

Writing to Application Insights is done asynchronously, usually every 30 seconds or 500 items. This means that you need to give your application some time to flush the cache before closing down. This can be done by configuring a ProcessExit method. This method is called when your Main() function exists.

This is a template where I initialize my TelemetryClient, and then flushes and waits 5 seconds for the cache to flush before the application is exited:

using System;
using System.Threading;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.DependencyInjection;

namespace MyCode
{
  internal class Program
  {
    public static void Main(string[] args)
    {
      // Initialize Service Collection
      var serviceCollection = new ServiceCollection();
      ConfigureServices(serviceCollection);

      // Initialize Service Provider
      var serviceProvider = serviceCollection.BuildServiceProvider();

      // Handling finalizing when process is ended
      AppDomain.CurrentDomain.ProcessExit += (s, e) => FinalizeApplication(serviceProvider);

      // Run Application
      // ... do run ...
    }

    private static void FinalizeApplication(ServiceProvider serviceProvider)
    {
      // Give TelemetryClient 5 seconds to flush it's content to Application Insights
      serviceProvider.GetService<TelemetryClient>().Flush();
      Thread.Sleep(5000);
    }

    private static void ConfigureServices(IServiceCollection serviceCollection)
    {
      // Add Application Insights
      var telemetryConfiguration = TelemetryConfiguration.CreateDefault();
      telemetryConfiguration.InstrumentationKey = "add-your-own-key";
      var telemetryClient = new TelemetryClient(telemetryConfiguration);
      serviceCollection.AddSingleton(telemetryClient);
    }

  }
}

HOW IT WORKS:

My .NET Core 3.1 console application is started by the Main() function. It starts by initializing the service collection. The only thing I do is adding a TelemetryClient to the collection, but this is where you would all all of your services.

The AppDomain.CurrentDomain.ProcessExit is configured to call the “FinalizeApplication” method, and this method will now be called whenever the Main() function exits.

The FinalizeApplication will then flush the TelemetryClient, and waits 5 seconds for the flush to do it’s magic.

Without the 5 second wait, I risk that the Flush is not done flushing before the thread is discontinued, hence the wait. For some reason, 5 seconds seems to be the magic threshold for a flush to do it’s thing.

MORE TO READ:

Posted in .NET Core, Microsoft Azure | Tagged , , | Leave a comment

Azure App Configuration – Getting the connection string from appsettings.json

Azure App Configuration is a services that provides a central place to manage application settings, and it provides cool features like feature flags and auto refresh of settings when they are changed.

Azure App Configuration

Azure App Configuration

To access Azure App Configuration from your .NET Core application, you include the preview NuGet Package:

Microsoft.Extensions.Configuration.AzureAppConfiguration

PLEASE NOTE: As per 2020-02-14, Azure App Configuration is still in beta. You must check the “include prerelease” button in Visual Studio to see the NuGet package.

To connect to the Azure App Configuration you need a connection string.

Most of the guides and help pages expects you get the connection string from an environment variable, or that you create a web host application and can get the connection string from a web config file.

But if you would like to use Azure App Configuration together with a local appsettings.json file, there is no help available.

The challenge is that the connectionstring to the Azure App Configuration is not available before you read and build a IConfiguration reading from the local appsettings.json. So you need some way to split the calls to ConfigurationBuilder and then merge the 2 together.

THE SCENARIO:

Let’s assume that you have a local appsettings.json file and that file includes the connectionstring (and maybe even some more settings that are not to be tampered with by those with access to the Azure App Configurations):

{
  "Logging": {
    "FileLogPath": "e:\somewhere\log_.txt"
  },
  "ConnectionStrings": {
    "AzureAppConfiguration": "Endpoint=https://xxxxxxxx.azconfig.io;Id=xxxxxxxx;Secret=xxxxxxxx",
  }
}

THE SOLUTION:

You then need to merge these settings with the settings from Azure App Configuration.

var localConfig = new ConfigurationBuilder()
 .SetBasePath(Directory.GetCurrentDirectory())
 .AddJsonFile("appsettings.json", false, true)
 .Build();

var configuration = new ConfigurationBuilder()
 .AddConfiguration(localConfig)
 .AddAzureAppConfiguration(
    options =>
    {
      options.Connect(configuration.GetConnectionString("AzureAppConfiguration"));
    }
  )
 .Build();

The localConfig variable contains the settings from appsettings.json. This configuration is then merged with the Azure App Configuration into the configuration variable, which is then the final IConfiguration that will be used in the application.

MORE TO READ:

Posted in .NET Core, c#, Microsoft Azure | Tagged , , , | Leave a comment

Command line parameters in .NET Core Console Applications

When implementing .NET Core console applications, you can extract the command line parameters from the void Main(string[] args) method, but it can be a little tedious to do so. To help with the parsing, Microsoft used to maintain the Microsoft.Extensions.CommandLineUtils, but this library have not been in development since 2017, so this is not a good option.

Thankfully, Nate McMaster forked the Microsoft project and created the CommandLineUtils, so developers like you and I don’t have to concern us with parsing of parameters, creating help pages, documentation and so on.

First you need to install the NuGet package:

McMaster.Extensions.CommandLineUtils

This is a base template of how your Program.cs main entry point will look like when using CommandLineUtils:

using McMaster.Extensions.CommandLineUtils;

namespace MyApplication
{
  [Command(Name = "The application name", Description = "The application description")]
  [HelpOption("-?")]
  public class Program
  {
    static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);

    [Argument(0, Description = "The first argument")]
    private string MyArgument { get; }

    [Option("-o|--option", Description = "An example parameter")]
    private bool MyOption { get; }
    
    private void OnExecute()
    {
      // Initialize all the things...
      // Configure logging, application insights, ...
      // Run your application
    }
  }
}

EXPLANATION: THE Main(string[] args) METHOD:

This function is now pointing to the “OnExecute()” function instead. Treat theOnExecute() method as you would treat the Main(string[] args) method and initialize the servicecollection, logging, dependencies as you would normally do it.

EXPLANATION: ATTRIBUTE [Argument(…..)] VS [Option(……)]

CommandLineUtils treat command line parameters differently.

The [Argument] attribute determines parameters without any prefix. They also have a fixed position in the list of parameters.

The the example above, the MyArgument MUST be the first parameter when the application is called. This is determined by the value zero (0) in the attribute:

[Argument(0, Description = "The first argument")]

The value of MyArgument is set only by the first parameter:

./myapplication.exe firstparameter

Here, the value of MyArgument will be “firstparameter“.

The [Option] attribute does not have a fixed position, but a prefix instead. The boolean value of MyOption will be FALSE if -o or -option is NOT set, but true if -o or -option is set:

./myapplication.exe -o

Here, the value of MyOption is TRUE.

Options can have string parameters as well:

[Option("-o2|--option2", Description = "Another Option")]
private string MyOption2 { get; } = "Hello World";

The value of MyOption2 is “Hello world” unless you set the value in a parameter:

./myapplication.exe -o2 AnotherValue

Here, the value of MyOption2 will be “AnotherValue“.

The library have tons of more options, from automatic help pages, prompts for yes/no/passwords, optional/required parameters and so on. Dig into the library and see for yourself. Happy coding.

MORE TO READ:

Posted in .NET Core, c# | Tagged , | Leave a comment

Sitecore Create/Read/Update/Delete/Copy/Move/Rename item – the beginners guide

New Sitecorians often ask the most trivial questions, and I am happy to answer them. This question popped up lately: How do you perform basic CRUD operations on your Sitecore items? Well, it’s easy:

READ ITEM (GET ITEM):

// Get item from content database:
Item item = Sitecore.Context.ContentDatabase.GetItem("/sitecore/content/home");
// Get item from ID:
Item item = Sitecore.Context.ContentDatabase.GetItem(new Sitecore.Data.ID("{9464f2c9-8490-40e9-a95b-17f8a5128da6}");
// Get item from named database
Item item = Sitecore.Configuration.Factory.GetDatabase("master").GetItem("/sitecore/content/home");

CREATE ITEM:

// You need to have a root item to create item under:
Item item = Sitecore.Context.ContentDatabase.GetItem("/sitecore/content/home");
// You also need a template to create the item from:
TemplateID template = new TemplateID(new ID("{434b38a2-b929-4a89-bbc8-a6b66281e014}"));
// Then you can create a new item:
Item newItem = item.Add("new item", template);
// If you wish to create an item based on a branch:
BranchId branch = new BranchId(new ID("{4f254169-7666-4c2e-8021-a05026d5a2e2}"));
Item newItem = item.Add("new item", branch);

UPDATE ITEM:

// You need to have an item to update:
// Remember to always update items in the MASTER database,
// NOT the WEB Database:
Item item = Factory.GetDatabase("master").GetItem("/sitecore/content/home");
// You then set the item in editing mode
item.Editing.BeginEdit();
try
{ 
  // Change the contents of the fields to update
  item.Fields["field"].Value = "new value";
  // End edit writes the updates to the database:
  item.Editing.EndEdit();
}
catch (Exception ex)
{
  // in case of an exception, you do not really
  // need to cancel editing, but it is good 
  // manners and it indicates that you know
  // what the code is doing
  item.Editing.CancelEdit();
}

DELETE ITEM:

// You need to have an item to delete:
// Remember to always update items in the MASTER database,
// NOT the WEB Database:
Item item = Factory.GetDatabase("master").GetItem("/sitecore/content/home");
// Remember that deleting one item also delete the children.
// The item.Recycle() moves the item to the recycle bin, whilst the 
// item.Delete() permanently deletes the item:
item.Recycle();

COPY ITEM:

// You need to have a source destination:
Item destinationItem = Factory.GetDatabase("master").GetItem("/sitecore/content/home");
// You also need an item to copy:
Item sourceItem = Factory.GetDatabase("master").GetItem("/sitecore/content/sourceitem");
// Then you can copy:
sourceItem.CopyTo(destinationItem, "new name");

MOVE ITEM:

// You need to have a source destination:
Item destinationItem = Factory.GetDatabase("master").GetItem("/sitecore/content/home");
// You also need an item to move:
Item sourceItem = Factory.GetDatabase("master").GetItem("/sitecore/content/sourceitem");
// Then you can copy:
sourceItem.MoveTo(destinationItem);

RENAME ITEM:

// You need to have an item to rename:
Item item = Factory.GetDatabase("master").GetItem("/sitecore/content/home");
item.Editing.BeginEdit();
item.Name = "new name";
item.Editing.EndEdit();

MORE TO READ:

Posted in .net, c#, General .NET, Sitecore 5, Sitecore 6, Sitecore 7, Sitecore 8, Sitecore 9 | Tagged , | 1 Comment

Sitecore use item:deleting event to avoid deletion of items

The Sitecore extendable model allows you to build business rules into your Sitecore solution. This is an example where I disallow deletion of certain items when a certain business rule is true.

This pattern could be implemented using a field or template validator, or by using the Sitecore workflow,  but it would require you to remember to add the validator to the correct template, and also requires that no one tampers with your template settings.

Sitecore have an item:deleting event that is fired before an item is deleted. The arguments include a Result.Cancel property that can be set to false, thus stopping the deletion of your item. So the implementation looks like this:

using System;
using Sitecore.Data.Items;
using Sitecore.Events;
using Sitecore.Web.UI.Sheer;

namespace MyProject
{
  public class OnItemDeletingValidator
  {
    public void OnItemDeleting(object sender, EventArgs args)
    {
      // get the item that is being moved
      Item item = Event.ExtractParameter(args, 0) as Item;
      
      if (item == null)
        return;

      if (item.Database.Name != "master")
        return;

      // Only run the rule on a certain template.
      // Replace the "MyTemplate" with the name of your own
      // template
      if (item.TemplateName != "MyTemplate")
        return;

      // Check to see if the business rule is true or false
      // If true, do nothing.
      // Implemement your own business rule here
      if (BusinessRule(item))
        return;
      
      // Alert the user and cancel the delete
      SheerResponse.Alert("You cannot delete this item because the business rule is false");
      ((SitecoreEventArgs)args).Result.Cancel = true;
    }
  }
}

The code above is just a pseudo code example. You need to implement your own business rule, and determine your own template.

Also remember to assign the code to the event using configuration:

<event name="item:deleting">
  <handler type="MyProject.OnItemDeletingValidator, MyProject" method="OnItemDeleting"/>
</event>

MORE TO READ:

Posted in .net, c#, Sitecore 6, Sitecore 7, Sitecore 8, Sitecore 9 | Tagged , , | Leave a comment

.NET Core Api – Catch exceptions using a middleware

In .NET Core, we are starting to get used to the fact that nothing comes for free, and in the world of ultimate freedom of choice, every feature needs to be implemented by us.

This includes error handling. In a previous post, .NET Core Catch Model Binding Exceptions, I explained how you can use ApiBehaviorOptions to catch exceptions thrown when someone POST the wrong JSON format to your endpoint.

It should be noted, that you can still catch exceptions in your endpoint using try…catch:

namespace MyCode.Controllers
{
  [ApiController]
  [Route("/api")]
  [Produces("application/json")]
  public class MyController : ControllerBase
  {
    [HttpPost()]
    public IActionResult LogDoNavigate([FromBody] MyModel model)
    {
      try
      {
	    ...
            ...
      }
      catch (Exception ex)
      {
        return base.BadRequest(new ProblemDetails() { Title = ex.Message, Detail = ex.ToString() });
      }
    }
  }
}

But what if the exception is not in this part of the Controller? What if the exception is in a System.Text.Serializion.JsonConverter that runs before your endpoint is called? The code will still throw an exception of course, but there is no immediate default handler, and the exception will not end up in your log file, or your Application Insights instance.

To handle such a scenario, you need to implement a Middleware. Middlewares are a piece of code that handles request and responses. You can use middlewares for many situations, but the most obvious are as exception handlers.

STEP 1: CREATE A CUSTOM MIDDLEWARE

This middleware will catch errors and return the exception as a JSON result object. You need to add your own logging or Application Insights code to write to log.

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace MyCode
{
  public class JsonExceptionMiddleware
  {
    public JsonExceptionMiddleware()
    {
    }

    public async Task Invoke(HttpContext context)
    {
      var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
      if (contextFeature != null && contextFeature.Error != null)
      {
        // ...
        // Add lines to your log file, or your 
        // Application insights instance here
        // ...
        context.Response.StatusCode = (int)GetErrorCode(contextFeature.Error);
        context.Response.ContentType = "application/json";

        await context.Response.WriteAsync(JsonConvert.SerializeObject(new ProblemDetails()
        {
          Status = context.Response.StatusCode,
          Title = contextFeature.Error.Message
        }));
      }
    }

    private static HttpStatusCode GetErrorCode(Exception e)
    {
      switch (e)
      {
        case ValidationException _:
          return HttpStatusCode.BadRequest;
        case FormatException _:
          return HttpStatusCode.BadRequest;
        case AuthenticationException _:
          return HttpStatusCode.Forbidden;
        case NotImplementedException _:
          return HttpStatusCode.NotImplemented;
        default:
          return HttpStatusCode.InternalServerError;
      }
    }
  }
}

STEP 2: ADD THE MIDDLEWARE TO YOUR IApplicationBuilder

Add the middleware to the Configure method in the Startup.cs file. Call the UseExceptionHandler method to use the middleware:

public void Configure(IApplicationBuilder app)
{
  ...
  ...
  app.UseExceptionHandler(new ExceptionHandlerOptions { 
    ExceptionHandler = new JsonExceptionMiddleware().Invoke 
    }
  );
  ...
  ...
}

You would expect the middleware to catch any exception. It does catch exceptions in your Controller code, so don’t need a try-catch handler in every Controller. But it does not catch model binding exceptions, so you still need to catch those.

MORE TO READ:

Posted in .NET Core, c#, Microsoft Azure | Tagged , , , | 3 Comments

Sitecore and Application Insights – How to remove 90% of all log lines without sacrificing traceability

In this article I will explain how you can remove up to 90% of all log lines from the Application Insights log, but still keep every log line in the file log. All of this without loosing any important information.

Sitecore uses a Log4Net Appender to inject log lines into Application Insights. If you take a look at the App_Config/Sitecore/Azure/Sitecore.Cloud.ApplicationInsights.config file that is deployed to your CM/CD servers, you will find the appender at the top, and it could look like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/" xmlns:role="http://www.sitecore.net/xmlconfig/role/"> 
<sitecore> 
  <log4net> 
    <root> 
      <appender-ref ref="ApplicationInsightsAppender" patch:after="appender-ref[@ref='LogFileAppender']" />
    </root> 
    <appender name="ApplicationInsightsAppender" type="Sitecore.Cloud.ApplicationInsights.Logging.LevelTraceAppender, Sitecore.Cloud.ApplicationInsights" patch:after="appender[@name='LogFileAppender']"> 
      <threshold value="INFO" /> 
        <layout type="log4net.Layout.PatternLayout"> 
          <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
        </layout> 
    </appender> 
  </log4net> 
</sitecore> 
</configuration>

Sitecore does log a lot of information that probably should have been marked as debug information. This is fine when logging to a log file. But with Application Insights there are issues with logging too much information:

  • You have to pay for what you log. Although Azure is priced reasonable, it is not free. Any excessive logging can make your budget explode.
  • You might run into  sampling issues (“Data received from your application is being sampled to reduce the volume of telemetry data retained; only sampled documents will be returned. The sampling may be applied by the Application Insights SDK or on ingestion by Application Insights.“). This can be disabled, but sampling usually are there for a reason, as Application Insights cost money the more lines you log.

When I analyze my log files, there are several lines that have never helped me:

ManagedPoolThread #6 00:02:46 INFO Job started: Job started: Index_Update_IndexName=sitecore_Master_index
ManagedPoolThread #9 00:04:48 INFO Scheduling.DatabaseAgent started. Database: master
ManagedPoolThread #13 00:04:48 INFO Cache created: 'ExperienceAnalytics.DimensionItems' (max size: 2MB, running total: 9090MB)

The “Job started” alone can take up to 90% of the log lines in a solution!

To remove these log lines from the Application Insights log, we can use the log4net.Filter.StringMatchFilter. This filter can add or remove lines containing a string.

Filters are added to the appender attribute in the config file like this:

<log4net>
  <root>
    <appender-ref ref="ApplicationInsightsAppender" patch:after="appender-ref[@ref='LogFileAppender']" />
  </root>
  <appender name="ApplicationInsightsAppender" type="Sitecore.Cloud.ApplicationInsights.Logging.LevelTraceAppender, Sitecore.Cloud.ApplicationInsights" patch:after="appender[@name='LogFileAppender']">
    <threshold value="INFO" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
    </layout>
    <filter type="log4net.Filter.StringMatchFilter">
      <stringToMatch value="Job started: Index_Update_IndexName=" />
      <acceptOnMatch value="false" />
    </filter>
    ...
    ...
  </appender>
</log4net>

The “stringToMatch” is the string that Log4Net looks for, and the acceptOnMatch=false ensure that the log line is not appended to the log.

I have found the following candidates for removal from the Application Insights log:

  • Job started: Index_Update_IndexName=
  • Job ended: Index_Update_IndexName=
  • Job started: UpdateIndexDocuments
  • Job ended: UpdateIndexDocuments
  • Job started: Sitecore.
  • Job ended: Sitecore.
  • Request is redirected to no layout page
  • HttpModule is being initialized
  • File is being deleted by cleanup task
  • Cache created:
  • Health.

There are probably more depending on your Sitecore installation. You should use the old but excellent Sitecore Log Analyzer to analyse the file logs and find the repetitive log lines that you never use, and filter them from the Application Insights log. Remember that what does not help me, might help you. But removing noise really does give a better overview, and your client’s wallet will be happy too :).

MORE TO READ:

 

Posted in Microsoft Azure, Sitecore 8, Sitecore 9 | Tagged , , , , , | 1 Comment

.NET Core Catch Model Binding Exceptions

In .NET Core, if you create an API project, and you have an controller receiving an object you defined yourself, the controller will not be called if you use the wrong model, or if the model is not in a correct JSON/XML format.

This means that you cannot catch the exception in your own try/catch block, which is annoying if you would like to log the exception in your own log file, or to your own Application Insights instance.

To catch model binding exceptions, you need to configure the ApiBehaviorOptions in your ConfigureServices(IServiceCollection services) method.

STEP 1: CALL services.Configure<ApiBehaviorOptions>:

I have made a private method in my Startup.cs file:

private void ConfigureModelBindingExceptionHandling(IServiceCollection services)
{
  services.Configure&lt;ApiBehaviorOptions&gt;(options =>
  {
    options.InvalidModelStateResponseFactory = actionContext =>
    {
      ValidationProblemDetails error = actionContext.ModelState
          .Where(e => e.Value.Errors.Count > 0)
          .Select(e => new ValidationProblemDetails(actionContext.ModelState)).FirstOrDefault();

      // Here you can add logging to you log file or to your Application Insights.
	  // For example, using Serilog:
	  // Log.Error($"{{@RequestPath}} received invalid message format: {{@Exception}}", 
	  //   actionContext.HttpContext.Request.Path.Value, 
	  //   error.Errors.Values);
      return new BadRequestObjectResult(error);
    };
  });
}

STEP 2: CALL THE METHOD AFTER LOG AND/OR APPLICATION INSIGHTS HAVE BEEN INSTANTIATED:

Sequence is important. In the public void ConfigureServices(IServiceCollection services) method, after you have instantiated logging, Application Insights, or whatever you use. Then call the new method:

public void ConfigureServices(IServiceCollection services)
{
  // Add logging, Application Insights, Controllers, ...
  ...
  ...
  // Add exception logging on model binding exceptions
  ConfigureModelBindingExceptionHandling(services, telemetryClient);
  ...
  ...
}

MORE TO READ:

Posted in .NET Core, c#, Microsoft Azure | Tagged , , | 1 Comment

Sitecore use global and environment variables in config files

The Sitecore config files are a complex machine and it requires a healthy mind to work with it. Fortunately Sitecore have implemented a few tricks to ease our pain. This article focuses on 2 parts: global variable replacement and environment variable replacement.

GLOBAL VARIABLES:

Global variables are defined once and can then be reused in all your config settings.

Maybe you have seen these variables:

<sitecore>
  <sc.variable name="dataFolder" value="/App_Data" />
  <sc.variable name="mediaFolder" value="/upload" />
  <sc.variable name="tempFolder" value="/temp" />
</sitecore>

The sc.variable element defines the variable. The variables can then be used where you need them by writing $(variable_name):

<file value="$(dataFolder)/logs/myfile.log"/>
<setting name="IndexFolder" value="$(dataFolder)/indexes" />

The above variables are just the built in ones. You can create your own variables and use them too.

ENVIRONMENT VARIABLES:

Since Sitecore 8.2 we have been blessed with environment variable replacements too.

Let’s say you have an environment variable on your machine:

Global Environment Variables

Global Environment Variables

You can add the environment variable to the config by writing $(env:variable_name):

<setting name="Environment" value="$(env:ASPNETCORE_ENVIRONMENT)" />

This is especially nifty as you can deploy the same config file to different environments and control your application per server.

MORE TO READ:

 

Posted in .net, General .NET, Sitecore 8, Sitecore 9 | Tagged , , , | 1 Comment