Microsoft Azure PlayFab logo
    • Multiplayer
    • LiveOps
    • Data & Analytics
    • Add-ons
    • For Any Role

      • Engineer
      • Designer
      • Executive
      • Marketer
    • For Any Stage

      • Build
      • Improve
      • Grow
    • For Any Size

      • Solo
      • Indie
      • AAA
  • Runs on PlayFab
  • Pricing
    • Blog
    • Forums
    • Contact us
  • Sign up
  • Sign in
  • Ask a question
  • Spaces
    • PlayStream
    • Feature Requests
    • Add-on Marketplace
    • Bugs
    • API and SDK Questions
    • General Discussion
    • LiveOps
    • Topics
    • Questions
    • Articles
    • Ideas
    • Users
    • Badges
  • Home /
  • Bugs /
avatar image
Question by esk · Aug 13, 2021 at 02:02 AM · CloudScript

Error when using local CloudScript on Azure Functions

When the playfab client API calls the locally running azure functions cloudscript from Unity, the response always gives an error

SerializationException: Invalid JSON string
PlayFab.Json.PlayFabSimpleJson.DeserializeObject (System.String json) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:570)
PlayFab.Json.PlayFabSimpleJson.DeserializeObject (System.String json, System.Type type, PlayFab.Json.IJsonSerializerStrategy jsonSerializerStrategy) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:602)
PlayFab.Json.PlayFabSimpleJson.DeserializeObject[T] (System.String json, PlayFab.Json.IJsonSerializerStrategy jsonSerializerStrategy) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:610)
PlayFab.Json.SimpleJsonInstance.DeserializeObject[T] (System.String json) (at Assets/PlayFabSDK/Shared/Internal/ISerializer.cs:85)
PlayFab.Internal.PlayFabUnityHttp.OnResponse (System.String response, PlayFab.Internal.CallRequestContainer reqContainer) (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:176)
UnityEngine.Debug:LogException(Exception)
PlayFab.Internal.PlayFabUnityHttp:OnResponse(String, CallRequestContainer) (at 

I have even tried returning a simple string from the function to the client, but it gives the same error. I am using the latest ExecuteFunction.cs that is provided here https://github.com/PlayFab/pf-af-devfuncs/blob/main/csharp/ExecuteFunction.cs, and have made a Github issue here https://github.com/PlayFab/pf-af-devfuncs/issues/12

It works fine when calling the live function on azure cloud, it's just locally it doesn't work.

This is very important as I need to develop locally without having to affect production! The local workflow is already a bit terrible, I have to create and delete a json file just to get it to use the local functions URL, I can't even change it from code!!!

Comment

People who like this

0 Show 3
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Rick Chen ♦ · Aug 13, 2021 at 10:26 AM 0
Share

Could you please provide the relevant code snippet of your Azure function and your unity client for us to diagnose? Here is a similar thread that you could look into: https://community.playfab.com/questions/8487/serializationexception-invalid-json-string-in-unit.html.

avatar image esk · Aug 13, 2021 at 01:50 PM 0
Share

It doesn't matter what I return from the azure function, a string, an empty object, an object with only alphanumeric keys and values, everything gives this error!

avatar image Rick Chen ♦ · Aug 17, 2021 at 07:59 AM 0
Share

I have done a test using the code and followed this document Tutorial: Local debugging for Cloudscript using Azure Functions to debug locally. But I cannot reproduce this error. This error seems to be reported from the Unity. Which version of the Unity, the PlayFab SDK and the PlayFab Unity Editor Extension you were using? Could you provide the relevant Unity code snippet for us to diagnose?

5 Replies

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by Jay Zuo · Dec 16, 2021 at 09:18 AM

The issue here might to be that while local debugging, Unity sets Accept-Encoding to "deflate, gzip" (This seems to be a black box, as in Rick's test, the Accept-Encoding is always set to "identity", so he cannot reproduce this issue and according to Unity doc, it's not recommended to set accept-encoding header and we should leave it for automatic handling).

When the request's Accept-Encoding accepts "gzip", ExecuteFunction will return compressed response body. However, it doesn't set corresponding Content-Encoding header, so Unity doesn't decompress the response body for you, which caused it to be an unreadable sting that can't be deserialized. And eventually, Unity SDK throws an "Invalid JSON string" error.

To fix this, we can modify CompressResponseBody method and add Content-Encoding header like:

// If client accepts gzip, compress
if (encodings.Contains("gzip", StringComparer.OrdinalIgnoreCase))
{
    using (var stream = new MemoryStream())
    {
        using (var gZipStream = new GZipStream(stream, CompressionLevel.Fastest, false))
        {
            gZipStream.Write(responseBytes, 0, responseBytes.Length);
        }
        responseBytes = stream.ToArray();
    }
    var content = new ByteArrayContent(responseBytes);
    content.Headers.ContentEncoding.Add("gzip");
    return content;
}

I've also submitted a PR https://github.com/PlayFab/pf-af-devfuncs/pull/13. Hope it helps.

Comment

People who like this

0 Show 0 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image

Answer by esk · Aug 20, 2021 at 02:02 AM

Unity 2020.3.15f2, PlayFab 2.112.210816 SDK, basically latest everything.

The code snippet is essentially the most basic executefunction example given by the docs,

PlayFabCloudScriptAPI.ExecuteFunction(new ExecuteFunctionRequest()
        {
            Entity = new PlayFab.CloudScriptModels.EntityKey()
            {
                Id = PlayFabSettings.staticPlayer.EntityId, //Get this from when you logged in,
                Type = PlayFabSettings.staticPlayer.EntityType, //Get this from when you logged in
            },
            FunctionName = Constants.CloudScriptFunctionNames.PublishLevel, //This should be the name of your Azure Function that you created.
            FunctionParameter = payload, //This is the data that you would want to pass into your function.
            GeneratePlayStreamEvent = false //Set this to true if you would like this call to show up in PlayStream
        }, (ExecuteFunctionResult result) =>
        {
            if (result.FunctionResultTooLarge ?? false)
            {
                Debug.Log("This can happen if you exceed the limit that can be returned from an Azure Function, See PlayFab Limits Page for details.");
                t.TrySetException(new System.Exception("Function result too large"));
                return;
            }
            Debug.Log($"The {result.FunctionName} function took {result.ExecutionTimeMilliseconds} to complete");
            Debug.Log($"Result: {result.FunctionResult.ToString()}");


        }, (PlayFabError error) =>
        {
            Debug.Log($"PlayFab Error: {error.GenerateErrorReport()}");
        });

Comment

People who like this

0 Show 4 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image esk · Aug 19, 2021 at 07:11 PM 0
Share

@Rick Chen

avatar image Rick Chen ♦ · Aug 23, 2021 at 09:58 AM 0
Share

I have used the code snippet you provide and done a test. I got the result successfully and I still cannot reproduce the issue.

y unity version is 2020.3.8f1, my PlayFabSDK version is 2.98.201027 and my PlayFab Editor Extensions version is 2.100.201207.

Have you used the PlayFab Editor Extensions in your project? If so, what is the version of it?

I will try on the version you mentioned. In the mean time, as it is hard to tell which part was causing the issue. Please follow the Debugging C# code in Unity to find out the specific part that was causing the issue.

local-af-debug.png (63.9 kB)
avatar image esk Rick Chen ♦ · Aug 23, 2021 at 08:35 PM 0
Share

Im using editor extensions version 2.110.210628. It may be worth pointing out that i'm on MacOS (Big Sur, intel), and using the Azure VSCode extension.

What are you returning from the azure function? I tried returning a class, and a string, neither of which work locally (but do when hosted)

avatar image esk Rick Chen ♦ · Aug 24, 2021 at 01:20 AM 0
Share

Could you also share the executefunction.cs that you are using? maybe that's the issue.

avatar image

Answer by Rick Chen · Aug 27, 2021 at 07:56 AM

Sorry for the late reply. Currently it is hard for us to do the test. My Azure function was returning the result (object) of GetEntityProfile API call. Here is the codesnippet of my local Azure Function:

        [FunctionName("ExecuteFunction")]
        public static async Task<HttpResponseMessage> ExecuteFunction(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "CloudScript/ExecuteFunction")] HttpRequest request, ILogger log)
        {
            // Extract the caller's entity token
            string callerEntityToken = request.Headers["X-EntityToken"];
            // Extract the request body and deserialize
            string body = await DecompressHttpBody(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
                };
            }
            // Create a FunctionContextInternal as the payload to send to the target function
            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
            };
            // Serialize the request to the azure function and add headers
            var functionRequestContent = new StringContent(PlayFabSimpleJson.SerializeObject(functionContext));
            functionRequestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            functionRequestContent.Headers.Add("X-EntityToken",callerEntityToken);
            var azureFunctionUri = "https://[your title id].playfabapi.com/Profile/GetProfile";
            var sw = new Stopwatch();
            sw.Start();
            // Execute the local azure function
            using (var functionResponseMessage =
                await httpClient.PostAsync(azureFunctionUri, functionRequestContent))
            {
                sw.Stop();
                long executionTime = sw.ElapsedMilliseconds;
                if (!functionResponseMessage.IsSuccessStatusCode)
                {
                    throw new Exception($"An error occured while executing the target function locally: FunctionName: {execRequest.FunctionName}, HTTP Status Code: {functionResponseMessage.StatusCode}.");
                }
                // Extract the response content
                using (var functionResponseContent = functionResponseMessage.Content)
                {
                    // Prepare a response to reply back to client with and include function execution results
                    var functionResult = new ExecuteFunctionResult
                    {
                        FunctionName = execRequest.FunctionName,
                        FunctionResult = await ExtractFunctionResult(functionResponseContent),
                        ExecutionTimeMilliseconds = (int)executionTime,
                        FunctionResultTooLarge = false
                    };
                    // Reply back to client with final results
                    var output = new PlayFabJsonSuccess<ExecuteFunctionResult>
                    {
                        code = 200,
                        status = "OK",
                        data = functionResult
                    };
                    // Serialize the output and return it
                    var outputStr = PlayFabSimpleJson.SerializeObject(output);
                    return new HttpResponseMessage
                    {
                        Content = new ByteArrayContent(CompressResponseBody(output, request)),
                        StatusCode = HttpStatusCode.OK
                    };
                }
            }
        }

Here is my code snippet of how the local function is called in Unity:

PlayFabCloudScriptAPI.ExecuteFunction(new ExecuteFunctionRequest()
        {
            Entity = new PlayFab.CloudScriptModels.EntityKey()
            {
                Id = PlayFabSettings.staticPlayer.EntityId, //Get this from when you logged in,
                Type = PlayFabSettings.staticPlayer.EntityType, //Get this from when you logged in
            },
            FunctionName = "Constants.CloudScriptFunctionNames.PublishLevel", //This should be the name of your Azure Function that you created.
            FunctionParameter = "payload", //This is the data that you would want to pass into your function.
            GeneratePlayStreamEvent = false //Set this to true if you would like this call to show up in PlayStream
        }, (ExecuteFunctionResult result) =>
        {
            if (result.FunctionResultTooLarge ?? false)
            {
                Debug.Log("This can happen if you exceed the limit that can be returned from an Azure Function, See PlayFab Limits Page for details.");
                //t.TrySetException(new System.Exception("Function result too large"));
                return;
            }
            Debug.Log($"The {result.FunctionName} function took {result.ExecutionTimeMilliseconds} to complete");
            Debug.Log($"Result: {result.FunctionResult.ToString()}");
        }, (PlayFabError error) =>
        {
            Debug.Log($"PlayFab Error: {error.GenerateErrorReport()}");
        });

As the resources for us is limited, it could take a while for us to set up the test. Your patience is appreciated.

Comment

People who like this

0 Show 0 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image

Answer by esk · Nov 22, 2021 at 05:33 AM

The solution I finally figured out is to set "Accept-Encoding" to "identity" in the extraHeaders argument when calling ExecuteFunction. There seems to be some problem with the gzip encoding, hopefully the PlayFab team will figure it out as this seems to be an issue quite a few people are having. I was personally having this issue when developing locally, but now I seem to have it when using the cloud hosted Azure Functions directly too, and I don't know why!

Comment

People who like this

0 Show 0 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image

Answer by Michael Urvan · Nov 23, 2021 at 01:29 AM

wow thank you for saving me so much time!

Since the compression only breaks the local testing - to fix this (so you don't have to make all your client calls pass the Accept-Encoding which might be breaking the normal cloud azure calls too), just modify your LocalExecuteFunction() code that they told us to add for local testing:

for DecompressHttpBody()

on the 3rd line down change it to:

if (true) //if (string.IsNullOrWhiteSpace(encoding))

for CompressResponseBody()

on the 8th line down change it to:

if (true)//if (string.IsNullOrEmpty(encodingsString))

Comment
CitaTo

People who like this

1 Show 1 · Share
10 |1200 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CitaTo · Dec 15, 2021 at 02:52 PM 0
Share

Thank you, your solution is worked for me.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Navigation

Spaces
  • General Discussion
  • API and SDK Questions
  • Feature Requests
  • PlayStream
  • Bugs
  • Add-on Marketplace
  • LiveOps
  • Follow this Question

    Answers Answers and Comments

    4 People are following this question.

    avatar image avatar image avatar image avatar image

    Related Questions

    CloudScript execution API requests issued (triggered action)), bug or how does it works? 1 Answer

    title_player_account PlayFabId does not work with cloud script 1 Answer

    CloudScript Error in Player Dashboard 2 Answers

    CloudScript won't deploy last revision 1 Answer

    Intermittent 500 Error on Azure Functions called via Playfab 1 Answer

    PlayFab

    • Multiplayer
    • LiveOps
    • Data & Analytics
    • Runs on PlayFab
    • Pricing

    Solutions

    • For Any Role

      • Engineer
      • Designer
      • Executive
      • Marketer
    • For Any Stage

      • Build
      • Improve
      • Grow
    • For Any Size

      • Solo
      • Indie
      • AAA

    Engineers

    • Documentation
    • Quickstarts
    • API Reference
    • SDKs
    • Usage Limits

    Resources

    • Forums
    • Contact us
    • Blog
    • Service Health
    • Terms of Service
    • Attribution

    Follow us

    • Facebook
    • Twitter
    • LinkedIn
    • YouTube
    • Sitemap
    • Contact Microsoft
    • Privacy & cookies
    • Terms of use
    • Trademarks
    • Safety & eco
    • About our ads
    • © Microsoft 2020
    • Anonymous
    • Sign in
    • Create
    • Ask a question
    • Create an article
    • Post an idea
    • Spaces
    • PlayStream
    • Feature Requests
    • Add-on Marketplace
    • Bugs
    • API and SDK Questions
    • General Discussion
    • LiveOps
    • Explore
    • Topics
    • Questions
    • Articles
    • Ideas
    • Users
    • Badges