Friday, November 29, 2019

ASP.NET ReportViewer - ReportViewerWebControl.axd was not found

After migrating an old ASP.NET application to a new IIS server, the application started to throw errors. The app was using a ReportViewer control to show some SQL Server Reporting Services reports. But when I tried to load a specific report, the report viewer kept showing a spinning wheel.

When I took a look at the console, I noticed 404 errors for ReportViewerWebControl.axd.

The problem was that the original application was hosted on IIS 6. To configure the ReportViewer in IIS6 an entry for the Http Handler was added inside the system.web <httpHandler> section. Starting from IIS 7.0 this section is no longer read. Instead we have to add the ReportViewerWebControl.axd to the system.webserver <handlers> section:

Thursday, November 28, 2019

SignalR–Automatic reconnect

With the release of .NET Core 3.0, Microsoft updated SignalR as well. One of the new features is the re-introduction of Automatic Reconnect. Automatic reconnect was part of the original SignalR for ASP.NET, but wasn’t available (until recently) in ASP.NET Core.
The JavaScript client for SignalR can be configured to automatically reconnect using the withAutomaticReconnect method on HubConnectionBuilder. It won't automatically reconnect by default.

Without any parameters, withAutomaticReconnect() configures the client to wait 0, 2, 10, and 30 seconds respectively before trying each reconnect attempt, stopping after four failed attempts.
You can configure the number of reconnect attempts before disconnecting and change the reconnect timing, by passing an array of numbers representing the delay in milliseconds to wait before starting each reconnect attempt:

Watch the video here:

Wednesday, November 27, 2019

Kubernetes Learning Path–50 days from zero to hero

Microsoft released a learning path for Kubernetes:

Kubernetes is taking the app development world by storm. By 2022, more than 75% of global organizations will be running containerized applications in production.* Kubernetes is shaping the future of app development and management—and Microsoft wants to help you get started with it today.

This guide is meant for anyone interested in learning more about Kubernetes. In just 50 days, you’ll understand the basics of Kubernetes and get hands-on experience with its various components, capabilities, and solutions, including Azure Kubernetes Service. Go from zero to hero with Kubernetes to set your company up for future app development success.

Go check it out here: https://azure.microsoft.com/en-us/resources/kubernetes-learning-path/

Tuesday, November 26, 2019

GraphQL - A love story

In case you’ve missed my presentation at VISUG XL, you can find the slides here:

Demo’s are available at Github: https://github.com/wullemsb/visugxlgraphqldemos

Monday, November 25, 2019

ProxyKit–Bad gateway error

We are using ProxyKit as an API proxy for our Geo services. But although it worked perfectly on our development environment and when talking to our GeoServer, it failed with the following exception when we tried to access an ASP.NET Core API through the same proxy:

502 Bad Gateway

This turned out to be a bug in ProxyKit version 2.2.1. ProxyKit sets an empty content when executing a GET request. ASP.NET Core doesn’t like this as it expects that GET requests doesn’t have a body.

As a work around I explicitly set the content to null when executing a GET request:

Remark: A new version 2.2.2 was released last week to fix the issue: https://github.com/damianh/ProxyKit/releases/tag/v2.2.2.

Friday, November 22, 2019

ASP.NET SessionState error: Provider must implement the class 'System.Web.SessionState.SessionStateStoreProviderBase'

After switching to the async SQL Session State Provider one of our projects failed to run. Instead we got a yellow-screen-of-death with the following message:

Provider must implement the class 'System.Web.SessionState.SessionStateStoreProviderBase'

I had to take one extra manual step to get the async provider up and running. I had to replace the default session state module by an async alternative:

Thursday, November 21, 2019

Postman–Working with data files

Instead of doing a manual cleanup of my local RabbitMQ cluster I decided to use the Management HTTP API and script the whole process.

I decided to use Postman and the collection runner data files support.

Let’s walk through the steps:

  • I started by creating a new Collection inside Postman:

  • Inside this collection I started adding DELETE requests to different objects(exchanges, queues,…) inside RabbitMQ

  • I used variables (recognizable with the double mustaches {{variableName}}) inside the URLs.
    • E.g. http://user:pw@localhost:15672/api/exchanges/%2F/{{exchange}}
  • These variables should be replaced by values from a JSON data file.
  • Create a JSON file containing a list of values. Unfortunately it is not possible to add the data file to your collection. So save it anywhere you want
    • Here is how the data file looked like for the above request:
  • Let’s now try to use the collection runner. Click on the Runner button at the top:

  • Select the collection you want to run on the left.

  • Also select a data file.
  • Click on the big blue run button to execute the requests in the collection. Parameters will be replaced using the values you’ve provided in the data file.

Wednesday, November 20, 2019

RabbitMQ - Management HTTP API

One nice extra you get when installing the RabbitMQ management plugin is a corresponding HTTP api that allows you to do everything that is possible from the management portal through the API.

Remark: API documentation here: https://rawcdn.githack.com/rabbitmq/rabbitmq-management/v3.8.1/priv/www/api/index.html

There is only one thing I had to figure out…

You can query a specific vhost inside RabbitMQ by adding a vhost section inside the API. An example: /api/queues/vhost/name

The problem was I had to target the default vhost which was named “/” by default.

Trying to use that “/” inside the api didn’t work and resulted in an object not found error: /api/queues///name

The correct way was to add some URL encoding magic: /api/queues/%2F/name


Tuesday, November 19, 2019

IdentityModel 4 - Request custom grant

While updating my training material about OIDC I noticed that the documentation of IdentityServer didn’t reflect the latest changes in the IdentityModel library.

The example found in the documentation was still using the old TokenClient together with a RequestCustomGrantAsync() method:

This method no longer exists in version 4 of IdentityModel. Instead you need to use the RequestTokenAsync() extension method on the HttpClient:

Monday, November 18, 2019

ADFS error - The audience restriction was not valid because the specified audience identifier is not present in the acceptable identifiers list of this Federation Service.

After adding a claims provider trust in ADFS, we got the following error message when trying to use the configured 3th party IP-STS.

The audience restriction was not valid because the specified audience identifier is not present in the acceptable identifiers list of this Federation Service.

User Action

See the exception details for the audience identifier that failed validation. If the audience identifier identifies this Federation Service, add the audience identifier to the acceptable identifiers list by using Windows PowerShell for AD FS.  Note that the audience identifier is used to verify whether the token was sent to this Federation Service. If you think that the audience identifier does not identify your Federation Service, adding it to the acceptable identifiers list may open a security vulnerability in your system.

Exception details:

Microsoft.IdentityServer.AuthenticationFailedException: ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris.

Audience: 'http://adfs4.example.be/adfs/services/trust' ---> Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException: ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris.

Audience: 'http://adfs4.example.be/adfs/services/trust'

To solve this problem we had to add the audience uri of our ADFS server to the list of acceptable identifiers(as well explained in the error message):

 set-ADFSProperties -AcceptableIdentifier 'http://adfs4.example.be/adfs/services/trust'

Friday, November 15, 2019

Using a scoped service inside a HostedService

When trying to resolve a scoped dependency inside a HostedService, the runtime returned the following error message:

System.InvalidOperationException: Cannot consume scoped service ‘IScoped’ from singleton ‘Microsoft.Extensions.Hosting.IHostedService’.

The problem is that the IHostedService is a singleton and is created outside a dependency injection scope. Trying to inject any scoped service(e.g.  an EF Core DbContext) will result in the error message above.

To solve this problem you have to create a dependency injection scope using the IServiceScopeFactory. Within this scope you can use the scoped services:

Thursday, November 14, 2019

IdentityServer 4 - CORS

If an endpoint is called via Ajax calls from JavaScript-based clients, CORS configuration is required.

This can be done by setting the AllowedCorsOrigins collection on the client configuration. IdentityServer will consult these values to allow cross-origin calls from the origins.

Remark: Be sure to use an origin (not a URL) when configuring CORS. For example: https://foo:123/ is a URL, whereas https://foo:123 is an origin.

Wednesday, November 13, 2019

MassTransit–Batch Message Consumption

A lesser known feature inside MassTransit is the support of batch messages. This can be a really nice feature if you want to combine a batch of high-volume smaller messages into a single atomic consumer.

How does this work?

MassTransit will combine multiple messages into a single consume by specifying a window, such as a message count (batch size), time period, or a combination of both.

There are 2 configurable limits:

  • Size: A limit specifying the maximum number of messages which can fit into a single batch will trigger once that many messages are ready to be consumed. The batch size must be less than or equal to any prefetch counts or concurrent message delivery limits in order reach the size limit.

  • Time: A limit specifying how long to wait for additional messages from the time when the first message is ready, after which the messages ready within that time are delivered as a single batch. The time limit should be well within the lock time of a message, including enough time to process the batch.

Batch configuration

To use the batching functionality, configure an extra receive endpoint and use the Batch method to configure the endpoint:

Batch consumption

The message batch is delivered as an array to the consumer, so that the existing behavior is maintained for middleware, factories, etc. An additional context is available on the payload, which can be used to discover details related to the batch. Instead of receiving a single message you get a Batch<T> of messages:

Remark: This feature is experimental.  Be sure to configure the transport with sufficient concurrent message capacity (prefetch, etc.) so that a batch can actually complete without always reaching the time limit.

Tuesday, November 12, 2019

Azure DevOps - Kanban board limits

Got a question from a customer about the Kanban board in Azure DevOps Server:

I noticed that on the Kanban board on the first and last column only a limited number of work items are shown. Is this something that can be configured?

Quick answer: no.

To limit the number of items on the board, the first and last column of your kanban board will only show 20 work items. To see more work items you need to use the Show more items link in the bottom.

Monday, November 11, 2019

ASP.NET Core 3.0 - Enable Authentication

Quick tip for anyone using ASP.NET Core 3.0 (especially when you did an upgrade from ASP.NET Core 2.x); if you want to enable authentication don’t forget to add the correct middleware. You need both UseAuthentication and UseAuthorization:

In earlier versions of ASP.NET Core, authorization support was provided via the [Authorize] attribute. Authorization middleware wasn't available. In ASP.NET Core 3.0, authorization middleware is required(!). Therefore the ASP.NET Core Authorization Middleware (UseAuthorization) should be placed immediately after UseAuthentication.

Add the UseAuthorization and UseAuthentication methods AFTER the UseRouting() but BEFORE the UseEndpoints():

A DefaultPolicy is initially configured to require authentication, so no additional configuration is required.

Tuesday, November 5, 2019

Azure DevOps NuGet error

When trying to restore a NuGet package inside an Azure DevOps Build it failed with the following error message:

2019-11-13T09:17:59.8686867Z ##[section]Starting: NuGet restore

2019-11-13T09:17:59.8813825Z ==============================================================================

2019-11-13T09:17:59.8813825Z Task         : NuGet Installer

2019-11-13T09:17:59.8813825Z Description  : Installs or restores missing NuGet packages

2019-11-13T09:17:59.8813825Z Version      : 0.2.31

2019-11-13T09:17:59.8813825Z Author       : Microsoft Corporation

2019-11-13T09:17:59.8813825Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkID=613747)

2019-11-13T09:17:59.8813825Z ==============================================================================

2019-11-13T09:18:01.6050815Z [command]C:\Windows\system32\chcp.com 65001

2019-11-13T09:18:01.6050815Z Active code page: 65001

2019-11-13T09:18:01.6070347Z Detected NuGet version 3.3.0.212 / 3.3.0

2019-11-13T09:18:01.6080113Z SYSTEMVSSCONNECTION exists true

2019-11-13T09:18:01.6080113Z [command]D:\b\4\agent\_work\_tasks\NuGetInstaller_333b11bd-d341-40d9-afcf-b32d5ce6f23b\0.2.31\node_modules\nuget-task-common\NuGet\3.3.0\NuGet.exe restore -NonInteractive D:\b\4\agent\_work\59\s\MestbankPortaal.sln -ConfigFile D:\b\4\agent\_work\59\s\nuget.config

2019-11-13T09:18:02.1119369Z MSBuild auto-detection: using msbuild version '14.0' from 'C:\Program Files (x86)\MSBuild\14.0\bin'.

2019-11-13T09:18:02.4713257Z Feeds used:

2019-11-13T09:18:02.4713257Z   C:\Users\tfsservice\AppData\Local\NuGet\Cache

2019-11-13T09:18:02.4713257Z   C:\Users\tfsservice\.nuget\packages\

2019-11-13T09:18:02.4713257Z   http://tfs:8080/tfs/DefaultCollection/_packaging/Feed/nuget/v3/index.json

2019-11-13T09:18:02.4713257Z   C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\

2019-11-13T09:18:02.4713257Z

2019-11-13T09:18:02.4986705Z Restoring NuGet package IAM.Security.2.6.1.

2019-11-13T09:18:45.2347882Z WARNING: Unable to find version '2.6.1' of package 'IAM.Security'.

2019-11-13T09:18:45.2572523Z Unable to find version '2.6.1' of package 'IAM.Security'.

2019-11-13T09:18:45.2816698Z ##[error]Error: D:\b\4\agent\_work\_tasks\NuGetInstaller_333b11bd-d341-40d9-afcf-b32d5ce6f23b\0.2.31\node_modules\nuget-task-common\NuGet\3.3.0\NuGet.exe failed with return code: 1

2019-11-13T09:18:45.2816698Z ##[error]Packages failed to install

2019-11-13T09:18:45.2855766Z ##[section]Finishing: NuGet restore

This was a package that was recently added to our private Azure Artifacts feed, but I was able to restore it without any problem on my local machine.

Eventually I found out that I could solve the problem by increasing the NuGet version used by the build process to version 4: