DEV Community

loading...
Cover image for Building a Software Survey using Blazor - Part 5 - Client IP

Building a Software Survey using Blazor - Part 5 - Client IP

redfolder profile image Mark Taylor Originally published at red-folder.com ・3 min read

I've decided to write a small survey site using Blazor. Part of this is an excuse to learn Blazor.

As I learn with Blazor I will blog about it as a series of articles.

This series of articles is not intended to be a training course for Blazor - rather my thought process as I go through learning to use the product.

Earlier articles in this series:


One concern I has about running a survey, was how to avoid the results being swayed by a single party providing responses over and over again.

The simplest tactic to spot this was to record the client's IP address of the respondent and look for any bad behavior during the reporting stage.

In theory that should be simple, I just record the client's IP address and then save it along with the response.

The Problem

Normally for ASP.Net applications you would get the client's IP from the HttpContext.

At the time of writing however, Blazor did not have access to the HttpContext - see this Github issue.

I suspect this is something that will be resolved in future versions (possibly .Net 5), but this left me with a problem, how to get the client's IP without having access to the HttpContext.

The Solution

The solutions was actually reasonably simple - even if it took a few extra hops to achieve it.

First of all I grab the relevant details BEFORE starting the Blazor app and then pass them in:

_Hosts.cshtml

        @{
            var requestDetails = new SoftwareSurvey.Models.RequestDetails
            {
                ConnectionIpAddress = HttpContext.Connection.RemoteIpAddress.ToString(),
                ForwardedIpAddress = HttpContext.Request.Headers.ContainsKey("X-Forwarded-For") ? HttpContext.Request.Headers["X-Forwarded-For"].ToString() : null
            };
        }
        <app>
            <component type="typeof(App)" render-mode="ServerPrerendered" param-RequestDetails="requestDetails" />
        </app>
Enter fullscreen mode Exit fullscreen mode

So before I invoke the Blazor app, I use standard ASP.Net functionality to populate a model with the RemoteIpAddress and any value in the X-Forwarded-For header. The X-Forwarded-For header should contain the original IP address if the request has come through a proxy.

While certainly not bulletproof, it was enough to capture silly attempts to game the responses.

The model was then passed in using the param-RequestDetails attribute notation. The param- prefix is a Blazor notation to then look for the parameter on the App object and populate it with the model.

App.razor

@inject SoftwareSurvey.Models.SurveyResponse _surveyResponse

...

@code {
    [Parameter]
    public SoftwareSurvey.Models.RequestDetails RequestDetails { get; set; }

    protected override Task OnInitializedAsync()
    {
        _surveyResponse.ConnectionIpAddress = RequestDetails.ConnectionIpAddress;
        _surveyResponse.ForwardedIpAddress = RequestDetails.ForwardedIpAddress;

        return base.OnInitializedAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

The model is passed into the App via the RequestDetails property. I can then use those details to populate the _surveyResponse object which is used to track the respondent's answers - and ultimately is saved away into Azure Cosmos DB.

Added extra, the query string

In a similar manner, its also a useful technique to record query string values.

As part of my automated testing, I wanted to record that it was a test, thus could later be filtered out during the reporting phase.

To achieve this I wanted to be able to specify ?IsTest in the url so that it was marked in the _surveryResponse object - and saved away to Azure Cosmos DB for that later reporting.

So I simply added the following to the initialisation of the RequestDetails model in the _Hosts.cshtml:

IsTest = HttpContext.Request.QueryString.HasValue && HttpContext.Request.QueryString.Value.Contains("IsTest", StringComparison.InvariantCultureIgnoreCase)
Enter fullscreen mode Exit fullscreen mode

And then set the property on the _surveyResponse object in the App.OnInitializedAsync:

_surveyResponse.IsTest = RequestDetails.IsTest;
Enter fullscreen mode Exit fullscreen mode

Is this a better approach than using the HttpContext directly?

The HttpContext can often be a cause of problems for unit testing - especially if the unit testing is being added to existing code.

I often find that I create a wrapper around the HttpContext and then injecting the wrapper in to avoid direct HttpContext references.

The constraint of not having the HttpContext is arguably a good thing to avoid relying on direct references (which you really shouldn't).

By effectively treating the data as just another dependency to be passed in (via the [Parameter]) you are forcing the App to be cleaner - and thus easier to test.

So while I doubt it will be long before the HttpContext is made available with Blazor - for now it maybe a useful pattern.

Discussion (0)

pic
Editor guide