59

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

davidrgh
  • 853
  • 2
  • 10
  • 17
  • Please post your function examples so far. What language are you writing them in? You can write Azure Functions in JavaScript against Node.js, but also C# for ASP.NET: https://visualstudiomagazine.com/articles/2017/04/01/implementing-webhooks-azure-functions.aspx – Dai Jul 19 '17 at 07:36
  • Sorry; I'm using C# to code the apps. I've added the code I've done. – davidrgh Jul 19 '17 at 07:49
  • How about curl? See https://stackoverflow.com/questions/18611903/how-to-pass-payload-via-json-file-for-curl – Winter Singha Apr 07 '19 at 13:16
  • Google took me here. I understand this is a test project, but it is worth mentioning Microsoft recommends to not expose connection strings in plain: "As a best security practice, store connection strings and other secrets in your function app settings.". Also, I would not return exception messages as they are generated on the server side; it could expose some information to the public you might want to keep private: You should have the log on the server side, so you can "filter" the error response to contain less details - for example return a custom error code for the most expected errors. – Ama Nov 11 '19 at 13:59

13 Answers13

90

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);
            
        }
Krzysztof Madej
  • 32,704
  • 10
  • 78
  • 107
Allen Zhang
  • 2,432
  • 2
  • 20
  • 31
  • Neat, thank you EDIT: As of now (12/2020), when creating a new function with http trigger, the scaffolding will provide you with the full code. – Shockwaver Dec 04 '20 at 11:49
  • we can remove **StreamReader** here and just use one liner code using **System.Text.Json**. ```MyClass myClass = await JsonSerializer.DeserializeAsync(req.Body);``` – shehanpathi Nov 07 '22 at 15:07
43

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();
Amor
  • 8,325
  • 2
  • 19
  • 21
12

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.

sveinungf
  • 841
  • 1
  • 12
  • 22
7

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

  3. 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");
        }
    }
    
Mukul Joshi
  • 166
  • 4
5

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!;
}
Martin Wickman
  • 19,662
  • 12
  • 82
  • 106
  • 1
    I found if I give Foo as int it will be OK and made into string. But if I add int Moo and send it as string; code 500 will arise. Thus far I might advise 'messing' with JSON deserialization? – Chef Gladiator Jul 05 '22 at 17:15
2

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;
S.Dav
  • 2,436
  • 16
  • 22
2

We can do it by just one line code using System.Text.Json.

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

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.

4c74356b41
  • 69,186
  • 6
  • 100
  • 141
1

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

Md Farid Uddin Kiron
  • 16,817
  • 3
  • 17
  • 43
0

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..");
        }
    }
}
Sapnandu
  • 620
  • 7
  • 9
0

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;
    ...
}
Matt
  • 6,264
  • 10
  • 54
  • 82
  • 2
    I like this approach, but there's an even easier answer. Simply declare the type of the req param as your entity class. See https://stackoverflow.com/a/52748884/1664556 – rockgecko Feb 04 '20 at 04:49
  • @rockgecko I thought I had tried this first, but I don't recall it working. Given your insight now, I'm going to try this again. – Matt Feb 04 '20 at 19:37
0

Here is the point --> https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp

    [FunctionName("LuckyNumber")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(
        AuthorizationLevel.Function,
        "get", "post",
        Route = "max/{max:int?}/min/{min:int?}")] HttpRequest req,
        int? max, <-- Parameter max
        int? min, <-- Parameter min
        ILogger log)
    {
        int? maxInternal = max;
        int? minInternal = min;
    }

PS: I´m using .NET 6

Felipe Augusto
  • 1,341
  • 1
  • 16
  • 18
0

Nobody here is making use of the "using" parameter to read the body? Here is how I'd do it

    public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req)
                {
        
                    PersonDTO person;
                    if (req.Body != null)
                    {
                        using (var sr = new StreamReader(req.Body))
                        {
                            string requestBody = await sr.ReadToEndAsync();
                            person = JsonConvert.DeserializeObject<PersonDTO>(requestBody);
                        }

// rest of your code logic 
            return new OkObjectResult(person);

        }
Enrico
  • 2,734
  • 1
  • 27
  • 40