How to pass parameters by POST to an Azure function?

C#Visual StudioAzurePostAzure Functions

C# Problem Overview


I'm trying to do a simple Azure Function to learn about it. There will be 3 functions:

  • 1 function to insert a row into a table of a database. This table will contain the current date and a string parameters typed by the user and passed by GET.
  • 1 function similar to the previous one, but passing the parameter by POST.
  • 1 function to read the table and show its content.

I've been able to do the first and the third ones. But I can't pass the parameter by POST. I've looked for examples but I couldn't run them with success. The client app is a Windows Forms one.

Could anyone show me an example anout how to pass parameters by POST to the function and how to read them?

Thank's in advance

EDIT:

Here's the code to pass the parameters by GET (this is working fine):

private void button2_Click(object sender, EventArgs e)
{
	string cadena = lsql1.Text + "?notas=" + tNotas.Text;

	try
	{
		HttpWebRequest req = (HttpWebRequest)WebRequest.Create(cadena);
		HttpWebResponse res = (HttpWebResponse)req.GetResponse();

		if (res.StatusCode == HttpStatusCode.OK)
		{
			MessageBox.Show("Grabado");
		}
		else
		{
			MessageBox.Show(res.StatusDescription);
		}
	}catch (WebException ex)
	{
		using (Stream s = ex.Response.GetResponseStream())
		{
			StreamReader sr = new StreamReader(s);
			string text = sr.ReadToEnd();
			text = text.Substring(1, text.Length - 2);
			sr.Close();
			text = text.Replace("\\", "");
			text = "{" + text + "}";
			Error mensajeError = JsonConvert.DeserializeObject<Error>(text);

			MessageBox.Show(mensajeError.ExceptionMessage);
		}
		
	}
}

And here's the code to receive it and do the insert (this is working too):

[FunctionName("sql1")]
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
	try
	{
		log.Info("C# HTTP trigger function processed a request.");

		var cnnString = "Server=SERVIDOR;Database=base_prueba;User ID =azure;Password=0000;Trusted_Connection=False;Encrypt=False;";

		using (SqlConnection connection = new SqlConnection(cnnString))
		{
			connection.Open();
			SqlCommand cmd = connection.CreateCommand();

			DateTime fecha = DateTime.Today;
			
			string notas = req.GetQueryNameValuePairs()
			.FirstOrDefault(q => string.Compare(q.Key, "notas", true) == 0)
			.Value;

			// insert a log to the database
			cmd.CommandText = "INSERT INTO Prueba_Azure (fecha, notas) VALUES ('" + fecha.ToString() + "', '" + notas + "')";
			cmd.ExecuteNonQuery();
		}

		// Get request body
		dynamic data = await req.Content.ReadAsAsync<object>();

		return name == req.CreateResponse(HttpStatusCode.OK, "Done");
	}
	catch (Exception ex)
	{
		HttpResponseMessage res = req.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
		return res;
	}
}

What I'm looking for is to to this by POST

C# Solutions


Solution 1 - C#

In case google took you here, this is how it's done in March 2019 (Azure Functions v3):

public static async void Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
            HttpRequest req,
            ILogger log)
        {
            var content = await new StreamReader(req.Body).ReadToEndAsync();

            MyClass myClass = JsonConvert.DeserializeObject<MyClass>(content);
            
        }

Solution 2 - C#

To get the request content from the request body(post request), you could use req.Content.ReadAsAsync method. Here is the code sample.

Sample request body.

{
    "name": "Azure"
}

Define a class to deserialize the post data.

public class PostData
{
    public string name { get;set; }    
}

Get the post data and display it.

PostData data = await req.Content.ReadAsAsync<PostData>();
log.Info("name:" + data.name);

Client side code to send the post request.

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("function-url");
req.Method = "POST";
req.ContentType = "application/json";
Stream stream = req.GetRequestStream();
string json = "{\"name\": \"Azure\" }";
byte[] buffer = Encoding.UTF8.GetBytes(json);
stream.Write(buffer,0, buffer.Length);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();

Solution 3 - C#

If you are using System.Text.Json, you can read the POST data in one line:

public static async Task Run(
	[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
	HttpRequest req,
	ILogger log)
{
	MyClass myClass = await JsonSerializer.DeserializeAsync<MyClass>(req.Body);
}

If you are using Newtonsoft.Json, see the answer by Allen Zhang.

Solution 4 - C#

For passing parameters as POST request, you need to do following things:

  1. Make Json model of the parameters that u need to pass,ex:

     {"UserProfile":{ "UserId":"xyz1","FirstName":"Tom","LastName":"Hank" }}
    
  2. Post your data model using client like POSTMAN

enter image description here

  1. Now you will get the posted content in HttpRequestMessage body, sample code is as follows:

     [FunctionName("TestPost")]
     public static HttpResponseMessage POST([HttpTrigger(AuthorizationLevel.Function, "put", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
     {
         try
         {
             //create redis connection and database
             var RedisConnection = RedisConnectionFactory.GetConnection();
             var serializer = new NewtonsoftSerializer();
             var cacheClient = new StackExchangeRedisCacheClient(RedisConnection, serializer);
    
             //read json object from request body
             var content = req.Content;
             string JsonContent = content.ReadAsStringAsync().Result;
    
             var expirytime = DateTime.Now.AddHours(Convert.ToInt16(ConfigurationSettings.AppSettings["ExpiresAt"]));
    
             SessionModel ObjModel = JsonConvert.DeserializeObject<SessionModel>(JsonContent);
             bool added = cacheClient.Add("RedisKey", ObjModel, expirytime); //store to cache 
    
             return req.CreateResponse(HttpStatusCode.OK, "RedisKey");
         }
         catch (Exception ex)
         {
             return req.CreateErrorResponse(HttpStatusCode.InternalServerError, "an error has occured");
         }
     }
    

Solution 5 - C#

The query string (name/value pairs) is by default sent in the HTTP message body of a POST request and not as query string. The GetQueryNameValuePairs method will parse the query string and will by default not work with POST request.

For the POST request you could use something similar to this:

var content = request.Content;
string contentInString = content.ReadAsStringAsync().Result;

Solution 6 - C#

You can just supply your custom data class as a parameter to the HttpTrigger argument. This way you don't have to mess with the json deserialization yourself:

public async Task<IActionResult> UpdateAccount(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "api/v1/accounts/{id:guid}")] 
        SomeData someData,  // <----- Post body ends up here automatically
        HttpRequest req,
        Guid id,
        ILogger log)
{
    log.LogInformation ("Got POST with " + someData.Foo);
}


public class SomeData
{
    public string Foo { get; set; } = null!;
}

Solution 7 - C#

You need to attach data to the body of the post request and process it properly:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log) {
    // This reads your post request body into variable "data"
    string data = await req.Content.ReadAsStringAsync();
    // Here you can process json into an object
    dynamic parsed = JsonConvert.DeserializeObject(data);

    return exitstring == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Something went wrong, sorry")
        : req.CreateResponse(HttpStatusCode.OK);
}

You can find a slightly different example here and the exact example here.

Solution 8 - C#

It can be done in following way with custom class

Azure Function

[FunctionName("PostParameterFunction")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, ILogger log)
   {
      log.LogInformation("C# HTTP trigger function processed a request.");

       try
        {
             // Convert all request perameter into Json object

                var content = req.Content;
                string jsonContent = content.ReadAsStringAsync().Result;
                dynamic requestPram = JsonConvert.DeserializeObject<RequestModel>(jsonContent);

                // Validate the required param

                if (string.IsNullOrEmpty(requestPram.FirstName))
                {
                    return req.CreateResponse(HttpStatusCode.OK, "Please enter First Name!");
                }
                if (string.IsNullOrEmpty(requestPram.LastName))
                {
                    return req.CreateResponse(HttpStatusCode.OK, "Please enter Last Name!");
                }


                //Create object for partner Model to bind the response on it

                RequestModel objRequestModel = new RequestModel();

                objRequestModel.FirstName = requestPram.FirstName;
                objRequestModel.LastName = requestPram.LastName;

                //Return Request Model

                return req.CreateResponse(HttpStatusCode.OK, objRequestModel);
         }
        catch (Exception ex)
         {

                return req.CreateResponse(HttpStatusCode.OK, "Cannot Create Request! Reason: {0}", string.Format(ex.Message));
         }

        }

Request Class:

 public class RequestModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

    }

Request Input:

{
	"FirstName": "Kiron",
	"LastName":"Test"
}

PostMan Output Example:

enter image description here

Solution 9 - C#

I have done a very simple example to get data using POST request in Azure Function App. Please find the following example.

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace MyFunctions
{
    public static class MyFunctionsOperations
    {
        [FunctionName("MyFunctionsOperations")]
        public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");
            var headers = req.Headers;
            string collection = headers.GetValues("collection").First();   //getting parameter from header
            
            CosmosdbOperation obj = new CosmosdbOperation();
            dynamic data = await req.Content.ReadAsAsync<object>();  //getting body content
            Boolean response = await obj.MyFunctionExecution(data.ToString(), collection);

            return (response)
                ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a proper argument in the request body")
                : req.CreateResponse(HttpStatusCode.OK, "Operation successfully executed..");
        }
    }
}

Solution 10 - C#

I like the WebApi approach of using [FromBody] attribute, so using IBinding I made my own. Now I can just pass in the object.

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
[Binding]
public sealed class FromBodyAttribute : Attribute
{
}

public class FromBodyBinding : IBinding
{
    private readonly ILogger logger;
    public FromBodyBinding(ILogger logger)
    {
        this.logger = logger;
    }
    public Task<IValueProvider> BindAsync(BindingContext context)
    {
        // Get the HTTP request
        var request = context.BindingData["req"] as DefaultHttpRequest;

        return Task.FromResult<IValueProvider>(new FromBodyValueProvider(request, logger));
    }

    public bool FromAttribute => true;


    public Task<IValueProvider> BindAsync(object value, ValueBindingContext context)
    {
        return null;
    }

    public ParameterDescriptor ToParameterDescriptor() => new ParameterDescriptor();
}

public class FromBodyBindingProvider : IBindingProvider
{
    private readonly ILogger logger;
    public FromBodyBindingProvider(ILogger logger)
    {
        this.logger = logger;
    }

    public Task<IBinding> TryCreateAsync(BindingProviderContext context)
    {
        IBinding binding = new FromBodyBinding(this.logger);
        return Task.FromResult(binding);
    }
}

public class FromBodyValueProvider : IValueProvider
{
    private HttpRequest request;
    private ILogger logger;

    public FromBodyValueProvider(HttpRequest request, ILogger logger)
    {
        this.request = request;
        this.logger = logger;
    }

    public async Task<object> GetValueAsync()
    {
        try
        {
            string requestBody = await new StreamReader(this.request.Body).ReadToEndAsync();
            object result = JsonConvert.DeserializeObject(requestBody);
            return result;
        }
        catch (System.Exception ex)
        {
            this.logger.LogCritical(ex, "Error deserializing object from body");

            throw ex;
        }
    }

    public Type Type => typeof(object);

    public string ToInvokeString() => string.Empty;
}

public class BindingExtensionProvider : IExtensionConfigProvider
{
    private readonly ILogger logger;
    public BindingExtensionProvider(ILogger<Startup> logger)
    {
        this.logger = logger;
    }

    public void Initialize(ExtensionConfigContext context)
    {
        // Creates a rule that links the attribute to the binding
        context.AddBindingRule<FromBodyAttribute>().Bind(new FromBodyBindingProvider(this.logger));
    }
}

Then inside your Startup.cs file, add the binding.

public class Startup : IWebJobsStartup
{
    public void Configure(IWebJobsBuilder builder)
    {
        JsonConvert.DefaultSettings = () =>
        {
            return new JsonSerializerSettings()
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                },
                Formatting = Formatting.Indented
            };
        };

        builder.Services.AddLogging();
        builder.AddExtension<BindingExtensionProvider>();

    }
}

Now you can just have a regular old class, just like WebApi!

[FunctionName("MyFunction")]
public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Binding.FromBody] dynamic data) // or you can change 'dynamic' to some class
{
    string username = data?.username;
    ...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondavidrghView Question on Stackoverflow
Solution 1 - C#Allen ZhangView Answer on Stackoverflow
Solution 2 - C#AmorView Answer on Stackoverflow
Solution 3 - C#sveinungfView Answer on Stackoverflow
Solution 4 - C#Mukul JoshiView Answer on Stackoverflow
Solution 5 - C#S.DavView Answer on Stackoverflow
Solution 6 - C#Martin WickmanView Answer on Stackoverflow
Solution 7 - C#4c74356b41View Answer on Stackoverflow
Solution 8 - C#Md Farid Uddin KironView Answer on Stackoverflow
Solution 9 - C#SapnanduView Answer on Stackoverflow
Solution 10 - C#MattView Answer on Stackoverflow