Hi @Kim Strasser
Thank you for reaching out to Microsoft Q&A.
The errors are expected whenever you migrate to .NET isolated worker. In the isolated model:
Use HttpRequestData / HttpResponseData (not ASP.NET Core’s HttpRequest / HttpResponseMessage).
Read headers via HttpHeadersCollection (TryGetValues), not request.Headers["Name"].
Use request.Url for host/port (instead of request.Host).
Create responses with request.CreateResponse(HttpStatusCode.X) and write to HttpResponseData.Body.
PlayFab’s tutorial and repo remain valid conceptually (local redirect to your function) but the sample was originally written against the in‑process HTTP types—so you need to adapt it for isolated.
Fixes mapped to your compile errors
Header access
string callerEntityToken = request.Headers.TryGetValues("X-EntityToken", out var vals)
? vals?.FirstOrDefault()
: null;
Body reading / decompression helper
Change your helper to accept HttpRequestData and read request.Body (a Stream). If the client sends Content-Encoding: gzip, decompress; otherwise read plain text.
Local URL construction
Use request.Url.Host and request.Url.Port (and http/https from request.Url.Scheme) to build the URI of the target Azure Function in the same app.
Response creation & compression
Create an HttpResponseData via request.CreateResponse(HttpStatusCode.OK). Optionally gzip the body if the client advertises Accept-Encoding: gzip and set Content-Encoding: gzip.
Isolated-worker version of ExecuteFunction (minimal, working)
This sample keeps your PlayFab flow (get entity profile, title entity token; invoke target local function; return result) but replaces ASP.NET types and response handling with isolated equivalents.
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using PlayFab.Internal;
using PlayFab.Json;
using PlayFab.ProfilesModels;
namespace PlayFab.AzureFunctions
{
public class LocalExecuteFunction
{
private const string DEV_SECRET_KEY = "PLAYFAB_DEV_SECRET_KEY";
private const string TITLE_ID = "PLAYFAB_TITLE_ID";
private const string CLOUD_NAME = "PLAYFAB_CLOUD_NAME";
private const string DefaultRoutePrefix = "api";
private static readonly HttpClient httpClient = new HttpClient();
private readonly ILogger _logger;
public LocalExecuteFunction(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger<LocalExecuteFunction>();
[Function("ExecuteFunction")]
public async Task<HttpResponseData> ExecuteFunction(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "CloudScript/ExecuteFunction")]
HttpRequestData request)
{
// 1) Caller token from headers (isolated: HttpHeadersCollection)
string callerEntityToken = request.Headers.TryGetValues("X-EntityToken", out var vals)
? vals?.FirstOrDefault()
: null;
// 2) Read/decompress body (isolated: HttpRequestData)
string body = await DecompressHttpBodyAsync(request);
var execRequest = PlayFabSimpleJson.DeserializeObject<ExecuteFunctionRequest>(body);
EntityKey entityKey = null;
if (execRequest.Entity != null)
{
entityKey = new EntityKey { Id = execRequest.Entity?.Id, Type = execRequest.Entity?.Type };
}
// 3) Build PlayFab function context
var functionContext = new FunctionContextInternal
{
CallerEntityProfile = await GetEntityProfile(callerEntityToken, entityKey),
TitleAuthenticationContext = new TitleAuthenticationContext
{
Id = Environment.GetEnvironmentVariable(TITLE_ID, EnvironmentVariableTarget.Process),
EntityToken = await GetTitleEntityToken()
},
FunctionArgument = execRequest.FunctionParameter
};
var functionRequestContent = new StringContent(PlayFabSimpleJson.SerializeObject(functionContext));
functionRequestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// 4) Construct local URL using request.Url (isolated)
string targetUrl = ConstructLocalAzureFunctionUri(execRequest.FunctionName, request.Url);
var sw = Stopwatch.StartNew();
using var targetResponse = await httpClient.PostAsync(targetUrl, functionRequestContent);
sw.Stop();
if (!targetResponse.IsSuccessStatusCode)
throw new Exception($"Local target failed: {execRequest.FunctionName}, status {targetResponse.StatusCode}");
var result = new ExecuteFunctionResult
{
FunctionName = execRequest.FunctionName,
FunctionResult = await ExtractFunctionResult(targetResponse.Content),
ExecutionTimeMilliseconds = (int)sw.ElapsedMilliseconds,
FunctionResultTooLarge = false
};
var output = new PlayFabJsonSuccess<ExecuteFunctionResult>
{
code = 200, status = "OK", data = result
};
// 5) Create isolated response; gzip if client asks for it
var response = request.CreateResponse(HttpStatusCode.OK);
await WriteCompressedJsonAsync(response, output, request);
return response;
}
// --- PlayFab calls (unchanged functional behavior) ---
private static async Task<EntityProfileBody> GetEntityProfile(string callerEntityToken, EntityKey entity)
{
var url = GetServerApiUri("/Profile/GetProfile");
var req = new StringContent(PlayFabSimpleJson.SerializeObject(new GetEntityProfileRequest { Entity = entity }));
req.Headers.Add("X-EntityToken", callerEntityToken);
req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using var httpResp = await httpClient.PostAsync(url, req);
string json = await httpResp.Content.ReadAsStringAsync();
var ok = PlayFabSimpleJson.DeserializeObject<PlayFabJsonSuccess<GetEntityProfileResponse>>(json);
if (ok?.data == null || ok?.code != 200)
throw new Exception($"GetEntityProfile failed: code {ok?.code}");
return ok.data.Profile;
}
private static async Task<string> GetTitleEntityToken()
{
var url = GetServerApiUri("/Authentication/GetEntityToken");
var secret = Environment.GetEnvironmentVariable(DEV_SECRET_KEY, EnvironmentVariableTarget.Process);
if (string.IsNullOrEmpty(secret))
throw new Exception("Set PLAYFAB_DEV_SECRET_KEY in local.settings.json.");
var content = new StringContent(PlayFabSimpleJson.SerializeObject(new AuthenticationModels.GetEntityTokenRequest()));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.Add("X-SecretKey", secret);
using var httpResp = await httpClient.PostAsync(url, content);
string json = await httpResp.Content.ReadAsStringAsync();
var ok = PlayFabSimpleJson.DeserializeObject<PlayFabJsonSuccess<AuthenticationModels.GetEntityTokenResponse>>(json);
return ok.data.EntityToken;
}
// --- Isolated-only helpers below ---
private static string GetServerApiUri(string endpoint)
{
var sb = new StringBuilder();
var title = Environment.GetEnvironmentVariable(TITLE_ID, EnvironmentVariableTarget.Process);
if (!string.IsNullOrEmpty(title)) sb.Append(title).Append(".");
var cloud = Environment.GetEnvironmentVariable(CLOUD_NAME, EnvironmentVariableTarget.Process);
if (!string.IsNullOrEmpty(cloud)) sb.Append(cloud).Append(".");
sb.Append("playfabapi.com");
return new UriBuilder("https", sb.ToString(), -1, endpoint).Uri.AbsoluteUri;
}
private static string GetRoutePrefix()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "host.json");
if (!File.Exists(path)) return DefaultRoutePrefix;
var hostFile = File.ReadAllText(path);
var model = PlayFabSimpleJson.DeserializeObject<HostJsonModel>(hostFile);
return model?.extensions?.http?.routePrefix ?? DefaultRoutePrefix;
}
private static string ConstructLocalAzureFunctionUri(string functionName, Uri appUrl)
{
string functionPath = $"{GetRoutePrefix()}/{functionName}";
var b = new UriBuilder
{
Scheme = appUrl.Scheme,
Host = appUrl.Host,
Port = appUrl.IsDefaultPort ? (appUrl.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? 443 : 80) : appUrl.Port,
Path = functionPath
};
return b.Uri.AbsoluteUri;
}
private static async Task<string> DecompressHttpBodyAsync(HttpRequestData request)
{
string encoding = request.Headers.TryGetValues("Content-Encoding", out var v) ? v.FirstOrDefault() : null;
if (string.IsNullOrWhiteSpace(encoding))
{
using var reader = new StreamReader(request.Body);
return await reader.ReadToEndAsync();
}
if (!encoding.Equals("gzip", StringComparison.OrdinalIgnoreCase))
throw new Exception($"Unsupported body compression: {encoding}. Only gzip is supported.");
byte[] bytes = StreamToBytes(request.Body);
using var ms = new MemoryStream(bytes);
using var gz = new GZipStream(ms, CompressionMode.Decompress);
using var reader2 = new StreamReader(gz);
return await reader2.ReadToEndAsync();
}
private static async Task WriteCompressedJsonAsync(HttpResponseData response, object obj, HttpRequestData request)
{
string json = PlayFabSimpleJson.SerializeObject(obj);
byte[] payload = Encoding.UTF8.GetBytes(json);
response.Headers.Add("Content-Type", "application/json");
string accept = request.Headers.TryGetValues("Accept-Encoding", out var v) ? v.FirstOrDefault() : null;
if (string.IsNullOrEmpty(accept) || accept.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) < 0)
{
await response.Body.WriteAsync(payload, 0, payload.Length);
return;
}
using var outStream = new MemoryStream();
using (var gz = new GZipStream(outStream, CompressionLevel.Fastest, leaveOpen: true))
gz.Write(payload, 0, payload.Length);
var compressed = outStream.ToArray();
response.Headers.Add("Content-Encoding", "gzip");
await response.Body.WriteAsync(compressed, 0, compressed.Length);
}
private static byte[] StreamToBytes(Stream input)
{
input.Seek(0, SeekOrigin.Begin);
using var output = new MemoryStream();
input.CopyTo(output);
return output.ToArray();
}
private static async Task<object> ExtractFunctionResult(HttpContent content)
{
string s = await content.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(s)) return null;
if (s.StartsWith("{") || s.StartsWith("[")) return PlayFabSimpleJson.DeserializeObject(s);
if (float.TryParse(s, out var f)) return f;
if (bool.TryParse(s, out var b)) return b;
return s;
}
}
// Models unchanged...
}
*.csproj (key bits)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <!-- or net7.0 -->
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.*" OutputItemType="Analyzer" />
</ItemGroup>
</Project>
Program.cs
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.Build();
host.Run();
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"PLAYFAB_TITLE_ID": "B55D",
"PLAYFAB_DEV_SECRET_KEY": "xxxxxxxxxx"