1
votes

I have one question, which could be pretty dumb but I spent almost a week on it. I want to create Azure Function with http trigger which will check that URL which request comes is in Azure table and if such url exists, it will redirect it to another url, which will be e.g. 3 values in a given db record. The redirection process and the process of retrieving data from the database when the record matches I have working, the only problem is how to get a specific value from a given line. How to get the value of the properties? Converting 'data' to a list doesn't work.

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Cosmos.Table;
using System.Linq;
using static System.Environment;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
using System.Collections.Generic;

namespace Company.Function
{
    public static class Redirect2
    {
        [FunctionName("Redirect2")]
        public static async Task<IActionResult> GetAll(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "todo")] HttpRequest req,
        [Table("tablename", Connection = "AzureWebJobsStorage")] CloudTable cloudTable,
        ILogger log)
    {
        string OriginUrl = "test.domain.com";

        TableQuery<TodoTable> query = new TableQuery<TodoTable>().Where(
        TableQuery.CombineFilters(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, OriginUrl),
        TableOperators.Or, TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, OriginUrl)));  

        var segment =  cloudTable.ExecuteQuery(query);
        var data = segment.Select(TodoExtensions.ToTodo);

        return new OkObjectResult(data);

    }
    }

}

public class TodoTable : TableEntity
{
    public string URL { get; set; }

    public string wwwURL { get; set; } 

    public string ClientURL { get; set; }
}

public class Todo
{
    public string URL { get; set; }

    public string wwwURL { get; set; } 

    public string ClientURL { get; set; }
}

public static class TodoExtensions
{
    public static TodoTable ToTable(this Todo todo)
    {
        return new TodoTable
        {
            PartitionKey = todo.URL,
            RowKey = todo.wwwURL,
            ClientURL = todo.URL
        };
    }

    public static Todo ToTodo(this TodoTable todoTable)
    {
        return new Todo
        {
            URL = todoTable.PartitionKey,
            wwwURL = todoTable.RowKey,
            ClientURL = todoTable.URL
        };
    }
}

1
Do you expect to have single match for url? - Vova Bilyachat
How you pass OriginUrl to your service via url ? - Vova Bilyachat
@VovaBilyachat I will redirect url to app service using dns. Yes, if there is a record there will be only one. The logic will be more like this -> the url in the browser redirects to the address of the appservice, the function checks if the url is in the database -> if so, it redirects the user to the url which will be 3 values of this record - Kivas
SO originUrl is domain which you will get from domain? req.Host.Value ? - Vova Bilyachat
Its not that. Data from your storage explorer. Open table and show data pls - Vova Bilyachat

1 Answers

2
votes

You messed up with partitions

 TableQuery<TodoTable> query = new TableQuery<TodoTable>().Where(
    TableQuery.CombineFilters(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, OriginUrl),
    TableOperators.Or, TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, OriginUrl)));  

So here you try to match by rowKey or partitionKey this will be costly since it will need to go via all data in your Table Storage. What you really want to do is always query single partition.

So what I would recommend you to do is to set PartitionKey lets say just to "Default". Then you always query single partition.

If I understand you correctly to make your code work you should do smth like this

public static async Task<IActionResult> GetAll(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "todo")]
        HttpRequest req,
        [Table("tablename", Connection = "AzureWebJobsStorage")]
        CloudTable cloudTable,
        ILogger log)
    {
        string OriginUrl = req.Host.Value;

         TableQuery<TodoTable> query = new TableQuery<TodoTable>().Where(
            TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition(nameof(TableEntity.PartitionKey), QueryComparisons.Equal, OriginUrl),
                TableOperators.And,
                TableQuery.GenerateFilterCondition(nameof(TableEntity.RowKey), QueryComparisons.Equal, OriginUrl)))
            .Take(1);

        var segment = cloudTable.ExecuteQuery(query);
        var data = segment.Select(TodoExtensions.ToTodo).FirstOrDefault();

         return new OkObjectResult(new { url = data?.URL});

    }