-1

Been stuck with that error till madness phases ... Please help

I have created an Azure Mobile Service .NET backend, and am now trying to call its Post function from a Xamarin Android client

I initialize and call the Insert async function (these are just snippets from my code)

private static IMobileServiceTable<Todo> _todoMobileServiceTable;

public static bool? InitializeAms()
{
    try
    {
        CurrentPlatform.Init();
        _mobileServiceClient = new MobileServiceClient(applicationUrl, applicationKey);

        _todoMobileServiceTable = _mobileServiceClient.GetTable<Todo>();

        return true;
    }
    catch (MalformedURLException malformedUrlException)
    {
        ReportHelper.Report(Tag, "There was an error creating the Mobile Service. Verify the URL", true, malformedUrlException);
    }
    catch (Exception exception)
    {
        ReportHelper.Report(Tag, "Error occurred during initialization of Azure Mobile Services", true, exception);
    }

    return null;
}

_todoMobileServiceTable.InsertAsync(Todo);

I get the following error when calling .InsertAsync(Todo)

The request could not be completed. (Not Found)

N.B:

  • Azure storage client is not available for xamarin yet, and I have no other choice other than to use this dirty fork which is 1 year old and is made for iOS not Android (although it works fine with azure mobile service javascript) https://github.com/zgramana/IOSAzureBlobUploader

  • It works if I use the browser 'try it out' button but it doesn't work when I call it from the xamarin client app.

  • It works from the xamarin client app if I use the javascript mobile service

  • This error occurs both on the local azure mobile service and the published one online

Here is the WebApiConfig class

namespace Service.Ams
{
    public static class WebApiConfig
    {
        public static void Register()
        {
            // Use this class to set configuration options for your mobile service
            ConfigOptions options = new ConfigOptions();

            // Use this class to set WebAPI configuration options
            HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));

            // To display errors in the browser during development, uncomment the following
            // line. Comment it out again when you deploy your service for production use.
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

            Database.SetInitializer(new ServiceAmsInitializer());
        }
    }

    public class ServiceAmsInitializer : ClearDatabaseSchemaIfModelChanges<ServiceAmsDbContext>
    {}
}

Here is the TableController class

namespace Service.Ams.Controllers
{
    public class TodoItemController : TableController<TodoItem>
    {
        protected override void Initialize(HttpControllerContext controllerContext)
        {
            base.Initialize(controllerContext);
            ServiceAmsDbContext serviceAmsDbContext = new ServiceAmsDbContext();
            DomainManager = new EntityDomainManager<TodoItem>(serviceAmsDbContext, Request, Services);
        }

        // GET tables/TodoItem
        [AuthorizeLevel(AuthorizationLevel.Admin)]
        public IQueryable<TodoItem> GetAllTodoItems()
        {
            return Query(); 
        }

        // GET tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
        [AuthorizeLevel(AuthorizationLevel.Admin)]
        public SingleResult<TodoItem> GetTodoItem(string id)
        {
            return Lookup(id);
        }

        // PATCH tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
        [AuthorizeLevel(AuthorizationLevel.Admin)]
        public Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
        {
             return UpdateAsync(id, patch);
        }

        // POST tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
        [AuthorizeLevel(AuthorizationLevel.Anonymous)]
        public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
        {
            string storageAccountName;
            string storageAccountKey;

            // Try to get the Azure storage account token from app settings.  
            if (
                !(Services.Settings.TryGetValue("STORAGE_ACCOUNT_NAME", out storageAccountName) |
                  Services.Settings.TryGetValue("STORAGE_ACCOUNT_ACCESS_KEY", out storageAccountKey)))
                Services.Log.Error("Could not retrieve storage account settings.");

            // Set the URI for the Blob Storage service.
            Uri blobEndpoint = new Uri(string.Format("http://127.0.0.1:10000/{0}/", storageAccountName));

            // Create the BLOB service client.
            CloudBlobClient blobClient = new CloudBlobClient(blobEndpoint, new StorageCredentials(storageAccountName, storageAccountKey));

            // Create a container, if it doesn't already exist.
            CloudBlobContainer container = blobClient.GetContainerReference(item.ContainerName);
            await container.CreateIfNotExistsAsync();

            // Create a shared access permission policy. 
            BlobContainerPermissions containerPermissions = new BlobContainerPermissions
            {
                PublicAccess = BlobContainerPublicAccessType.Blob
            };

            // Enable anonymous read access to BLOBs.
            container.SetPermissions(containerPermissions);

            // Define a policy that gives write access to the container for 5 minutes.                                   
            SharedAccessBlobPolicy sasPolicy = new SharedAccessBlobPolicy
            {
                SharedAccessStartTime = DateTime.UtcNow,
                SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),
                Permissions = SharedAccessBlobPermissions.Write
            };

            // Get the SAS as a string.
            item.SasQueryString = container.GetSharedAccessSignature(sasPolicy);

            // Set the URL used to store the image.
            item.ImageLqUri = string.Format("{0}{1}/{2}", blobEndpoint, item.ContainerName, item.ResourceNameLq);
            item.ImageHqUri = string.Format("{0}{1}/{2}", blobEndpoint, item.ContainerName, item.ResourceNameHq);

            // Complete the insert operation.
            TodoItem current = await InsertAsync(item);
            return CreatedAtRoute("Tables", new {id = current.Id}, current);
        }

        // DELETE tables/TodoItem/55D11C86-6EA6-4C44-AA33-337FC9A27525
        [AuthorizeLevel(AuthorizationLevel.Admin)]
        public Task DeleteTodoItem(string id)
        {
             return DeleteAsync(id);
        }

    }
}

Here is the EntityData class

namespace Service.Ams.DataObjects
{
    [Table("dbo.TodoItems")]
    public class TodoItem : EntityData
    {
        public string ContainerName { get; set; }
        public string ResourceNameLq { get; set; }
        public string ResourceNameHq { get; set; }
        public string SasQueryString { get; set; }
        public string ImageLqUri { get; set; }
        public string ImageHqUri { get; set; }
    }
}
Mohamed Heiba
  • 1,813
  • 4
  • 38
  • 67

2 Answers2

1

Is there any way you can get a dump of what the HTTP request looks like?

I don't have an android client handy here but we can have a look on Monday.

Henrik

Henrik Frystyk Nielsen
  • 3,092
  • 1
  • 19
  • 17
  • Hello Henrik, sorry to be calling you for rescue on a weekend, just really halted by this. Ok I don't know how to get a dump of the HTTP request but i'll try to search for sth and get back to you now. – Mohamed Heiba Jun 22 '14 at 18:32
  • Hello @Henrik, I'm still trying to get tcpdump off Android. I spent yesterday rooting it, but still need to find a way to get a tcpdump. Might not be possible as the wifi interface on android don't always support monitoring mode. Any luck from your end ? – Mohamed Heiba Jun 23 '14 at 13:03
  • You're a genius. I spent the night rooting my phone, and trying hard to get a tcpdump, in the end tpackettracer app did it for me. I found that the problem was a naming conflict between the model class in the client and the model class on the Azure mobile service. I found that from the tcpdump using WireShark. Was a great learning experience. Thanks a lot. All working fine now – Mohamed Heiba Jun 23 '14 at 14:53
0

TableController and client corresponding class must have the same name for example TodoController and TodoClass. I don't know if there is an attribute that modifies this rule and how to use, if at server side decorating TableController class or at client side decorating data class.

Opal
  • 81,889
  • 28
  • 189
  • 210