question

Kim Strasser avatar image
Kim Strasser asked

Azure Function: Object reference not set to an instance of an object

I get this error message in PlayStream when I call the function 'ForgotPassword':

"Payload": {
        "Source": "API",
        "FunctionName": "ForgotPassword",
        "ExecutionTimeMilliseconds": 0,
        "Result": {
            "ErrorCode": "CloudScriptAzureFunctionsHTTPRequestError",
            "Message": "Invocation of cloud script function ForgotPassword failed",
            "ErrorAndMessage": {
                "Item1": "CloudScriptAzureFunctionsHTTPRequestError",
                "Item2": "Invocation of cloud script function ForgotPassword failed"
            }
        },
        "ResultTooLarge": false

And in Azure Portal:

2020-09-22T11:18:09.260 [Information] Executing 'ForgotPassword' (Reason='This function was programmatically called via the host APIs.', Id=fdf603b0-8d9f-462b-a38d-5a78d3e40cce)
2020-09-22T11:18:09.289 [Information] Run C# HTTP trigger function processed a request.
2020-09-22T11:18:09.330 [Information] The new password = ...
2020-09-22T11:18:09.331 [Information] The contact Email = ...
2020-09-22T11:18:09.331 [Information] TitleId = ...
2020-09-22T11:18:09.331 [Information] DeveloperSecretKey = ...
2020-09-22T11:18:09.593 [Error] Executed 'ForgotPassword' (Failed, Id=fdf603b0-8d9f-462b-a38d-5a78d3e40cce, Duration=334ms)Object reference not set to an instance of an object.

What is wrong with the function 'ForgotPassword'?

In addition, the Function URL is very long.

https://myplayfabfunctionapp.azurewebsites.net/api/ForgotPassword?code=JSXUqdml10yXBuZFYWVIcg%2FjMClz6PltkH7uYuPFpO2kmH0%2FjbU4tA%3D%3D

Is it normal that the Function URL is so long? I thought that the Function URL would always end with the function name, for example: https://myplayfabfunctionapp.azurewebsites.net/api/ForgotPassword

But in this case it doesn't end with the function name. I have registered the long Function URL and the Function name in my title account-->Functions-->Register Function.

Client code:

var result = await PlayFabCloudScriptAPI.ExecuteFunctionAsync(new ExecuteFunctionRequest()
{
    Entity = new PlayFab.CloudScriptModels.EntityKey()
    {
        Id = entityid,
        Type = entitytype,
    },
    FunctionName = "ForgotPassword",
    FunctionParameter = new { contactEmail = contactmail, newPassword = newpassword },
    GeneratePlayStreamEvent = true
});

Azure Function code:

namespace My.Functions
{
    public static class NewFunction
    {
        [FunctionName("ChangeDisplayname")]
        public static async Task<dynamic> MakeApiCall(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestMessage req, ILogger log)
        {
            /* Create the function execution's context through the request */
            var context = await FunctionContext<dynamic>.Create(req);
            var args = context.FunctionArgument;

            var desireddisplayname = args["NewDisplayname"];
 
            /* Create the request object through the SDK models */
            var request = new UpdateUserTitleDisplayNameRequest();
            request.PlayFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId;
            request.DisplayName = desireddisplayname;
            /* Use the ApiSettings and AuthenticationContext provided to the function as context for making API calls. */
            var adminApi = new PlayFabAdminInstanceAPI(context.ApiSettings, context.AuthenticationContext);
         
            /* The PlayFabServerAPI SDK methods provide means of making HTTP request to the PlayFab Main Server without any 
             * extra code needed to issue the HTTP requests. */
            return await adminApi.UpdateUserTitleDisplayNameAsync(request);
        }

    [FunctionName("ForgotPassword")]
    public static async Task<dynamic> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
    {
        log.LogInformation($"{nameof(Run)} C# HTTP trigger function processed a request.");
        
        FunctionContext<dynamic> context = JsonConvert.DeserializeObject<FunctionContext<dynamic>>(await req.ReadAsStringAsync());
        dynamic args = context.FunctionArgument; 
        var contactEmail = args["contactEmail"];
        var newPassword = args["newPassword"]; // retrieve the new password sent by the client, specified in ExecuteFunction -> FunctionParameter -> newPassword
        log.LogInformation($"The new password = {newPassword}");
        log.LogInformation($"The contact Email = {contactEmail}");
        //set up title id and secret key for admin api to use 
        var adminApiSettings = new PlayFab.PlayFabApiSettings()
        {
            TitleId = "...",
            DeveloperSecretKey = "...",
        };
        log.LogInformation($"TitleId = {adminApiSettings.TitleId}");
        log.LogInformation($"DeveloperSecretKey = {adminApiSettings.DeveloperSecretKey}");
        var adminAPI = new PlayFab.PlayFabAdminInstanceAPI(adminApiSettings);
        //get the Playfab ID from the contact email
        var userAccountInfoRequest = new LookupUserAccountInfoRequest
        {
            Email = contactEmail,       
        };
        var userAccountInfoDataResponse = await adminAPI.GetUserAccountInfoAsync(userAccountInfoRequest);
        string playfabID = userAccountInfoDataResponse.Result.UserInfo.PlayFabId;
        log.LogInformation($"PlayFabId = {playfabID}");
        //log.LogInformation($"PlayFabId = {context.CallerEntityProfile.Entity.Id}");
        //log.LogInformation($"PlayFabId = {context.CallerEntityProfile.Lineage.MasterPlayerAccountId}");
        //get player's token stored in internal data
        var getUserInternalDataResponse = await adminAPI.GetUserInternalDataAsync(new PlayFab.AdminModels.GetUserDataRequest(){
            PlayFabId = playfabID,
            //PlayFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId,
            Keys = new List<string> () {"Token"}
        });
 
        var tokenId = getUserInternalDataResponse.Result.Data["Token"].Value;
        log.LogInformation($"tokenId = {tokenId}");
        //reset the password
        return await adminAPI.ResetPasswordAsync(new PlayFab.AdminModels.ResetPasswordRequest() {
            Password = newPassword,
            Token = tokenId
        });
    }
}
}
CloudScript
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

1 Answer

·
Sarah Zhang avatar image
Sarah Zhang answered

This error means your “adminAPI” hasn’t instantiated successfully. Have you tried to provide the correct “TitleId” and “DeveloperSecretKey” to the PlayFabAPISettings? When we use the actual “TitleId” and “SecretKey”, this function can work fine.

//set up title id and secret key for admin api to use 
var adminApiSettings = new PlayFab.PlayFabApiSettings()
{
	TitleId = "[YourTitleId]",
	DeveloperSecretKey = "[YourSecretKey]"
};

Besides, the “Email” that used in the GetUserAccountInfo API should be “LoginEmail”.

>> Is it normal that the Function URL is so long?

Yes, it’s normal. When your Function’s access right is “Anonymous”, the URL would be short. When your Function’s access right is “Function”, the URL would contain the authorization key, so it would be long. For more details about Azure Fucntion’s access right, please refer to the Azure Function’s documentation.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Kim Strasser avatar image Kim Strasser commented ·

It works now.

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.