Replicate the Gateway found in ServiceStack.Razor pages

I am building a Blazor Server web app because I want to avoid the added complexity of a WASM app for now. With the razorformat pages, there was a property, Gateway, that would allow you to call other services from within the .cshtml page. However I can’t find something similar for .razor pages. So I am currently injecting a Gateway when configuring the AppHost:

services.AddSingleton(GetServiceGateway());

This works mostly without issue, however there seems to be an issue in conjunction with AutoApply userauth properties. When I try to create a new record, this error shows in the console:

 Error in GetSession() when ApplyPreAuthenticateFilters
  System.NullReferenceException: Object reference not set to an instance of an object.
 at ServiceStack.Auth.NetCoreIdentityAuthProvider.PreAuthenticateAsync(IRequest req, IResponse res) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/Auth/NetCoreIdentityAuthProvider.cs:line 114
 at ServiceStack.ServiceStackHost.ApplyPreAuthenticateFiltersAsync(IRequest httpReq, IResponse httpRes) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/ServiceStackHost.Runtime.cs:line 86
 at ServiceStack.ServiceExtensions.GetSessionInternalAsync(IRequest httpReq, Boolean reload, Boolean async, CancellationToken token) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/ServiceExtensions.cs:line 235

The record will be created, but CreatedBy string is null. Below an excerpt of my audit filter

else if (meta.HasAutoApply(Behavior.AuditCreate))
            {
                meta.Add(new AutoPopulateAttribute(nameof(AuditBase.DateCreated)) {
                    Eval = "now", NoCache = true
                });
                meta.Add(new AutoPopulateAttribute(nameof(AuditBase.CreatedBy)) {
                    Eval = "userAuthName", NoCache = true
                });
            }

Here is the auditbase and the dto

public class CustomerCallEventNote:AuditBase
{
    [AutoIncrement]
    public int Id { get; set; }
    [References(typeof(CustomerCallEvent))]
    public int CustomerCallEventId { get; set; }
    public string Text { get; set; }
}

public abstract class AuditBase
{
    [DataMember(Order = 1), Required]
    public DateTime DateCreated { get; set; }

    [DataMember(Order = 2)]
    public string CreatedBy { get; set; }

    [DataMember(Order = 3)]
    public DateTime? DateModified { get; set; }

    [DataMember(Order = 4)]
    public string ModifiedBy { get; set; }

    [DataMember(Order = 5), Index] //Check if Deleted
    public DateTime? DateDeleted { get; set; }

    [DataMember(Order = 6)]
    public string DeletedBy { get; set; }
}

I am using MicrosoftGraphProvider for authentication however is happens when I log in as the admin as well. When I use the locode ui to create records, the createdby field gets populated correctly.

I added an OnBeforeCreateAsync method to the AutoQueryFeature and I see that the Gateway requests are typed as BasicRequests, while the locode requests are typed NetCoreRequest. Is there some way I can alter the injected Gateway to be based on the proper request?

Hi @bgiromini,

Just to be clear, this is your own implementation of AuditBase used with your own use of AutoApply for Blazor Server? It’s hard to debug if we can’t reproduce, so if you are able to create a reproduction of the issue including any relevant custom code, I’ll take a look.

At a guess regarding the Eval = "userAuthName" not resolving, have a look to make sure the Script context has those scripts registered. This is done in the ServiceStackHost as standard, but from the code you’ve provided, I can’t tell if/how IoC dependencies are being resolved from your AppHost correctly.

The exact issue is due to the the IServiceGateway being injected did not have the proper NetCoreRequest which contains all the auth cookies. It was coming in as a basicrequest, which is why it could not find my session to determine the userauthname.

I discovered how to inject the HttpContext and now have working code:

builder.Services.AddHttpContextAccessor();
...
@inject IHttpContextAccessor httpContextAccessor    
...
var request = httpContextAccessor.HttpContext.ToRequest();
var gateway = new InProcessServiceGateway(request);
...
 api = await gateway.ApiAsync(myrequest);

So far the results are promising, just not sure it is the best way.

2 Likes

Ahh thanks for the clarification on the injection! Sure it will help others if they are doing the same thing!

Unless there’s an alternative way to access the HttpContext from a BlazorServer page without using the singleton, but judging from these answers it doesn’t look like there is.

To resolve the configured gateway you should use:

var gateway = HostContext.AppHost.GetServiceGateway(request);
1 Like