Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpTrigger with complex type does not work #164

Closed
swilkodev opened this issue Feb 24, 2021 · 10 comments · Fixed by #1712
Closed

HttpTrigger with complex type does not work #164

swilkodev opened this issue Feb 24, 2021 · 10 comments · Fixed by #1712
Assignees
Labels
area: http Items related to experience improvements for HTTP triggers

Comments

@swilkodev
Copy link

swilkodev commented Feb 24, 2021

Using the below HttpTrigger signature with preview 4.

[FunctionName(nameof(CreateTodosList))]
public async Task<HttpResponseData> CreateTodosList([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "todolists")]
TodoList todoList,
HttpRequestData req,
FunctionExecutionContext executionContext)
{
``
}

TodoList instance is always null. Even HttpRequestData instance is null.

This signature works fine with .NET core 3.1 solution with HttpRequest.

@fabiocav
Copy link
Member

Thank you @swilkodev . We have an update dropping this week bringing a lot of binding enhancements and fixes. This scenario might still not be addressed, but we'll use this to track.

@fabiocav
Copy link
Member

fabiocav commented Mar 4, 2021

This has been updated and the instructions on how to get the new version are here: #223

Closing as resolved, but please let us know if you continue to have issues. Thank you!

@fabiocav fabiocav closed this as completed Mar 4, 2021
@swilkodev
Copy link
Author

swilkodev commented Mar 4, 2021

I tried preview 5, however the same issue persists where the complex type (TodoList) and HttpRequestData is null.

I tried swapping the parameters around, and found HttpRequestData was then initialised correctly, however the complex type is still null. I think you should keep this issue open.

I'd also recommend adding an example to the sample app which uses a complex type with http trigger.

@fabiocav
Copy link
Member

fabiocav commented Mar 4, 2021

Request data wouldn’t be populated separately, but the POCO binding should work. Can you share your POCO and the sample request you’re sending?

@fabiocav fabiocav reopened this Mar 4, 2021
@swilkodev
Copy link
Author

swilkodev commented Mar 5, 2021

POCO class

public class TodoList
{
public string Title { get; set; }
}

Below is the request I'm sending in. I'm using REST Client in VS Code.

POST http://localhost:7071/api/todolists
Content-Type: application/json

{
"title": "My Todo List"
}

I'd also expect both the HttpRequestData and POCO binding to work together. The HttpRequestData object may be required where you want explicit control when creating the HttpResponseData.

@eriklieben
Copy link

eriklieben commented Mar 6, 2021

Throws an exception when using code at commit 4f988d3

Error converting 1 input parameters for Function 'HttpRequestWithPOCO': Cannot convert input parameter 'book' to type 'FunctionApp.Function1+Book' from type 'Microsoft.Azure.Functions.Worker.GrpcHttpRequestData'

The context.Source is of the type GrpcHttpRequestData which isn't matching string or ReadOnlyMemory<byte> so it skips trying to deserialize.

if (context.Source is string sourceString)

Adding a quick hack to let it perform the same thing as it does for strings, fixes it:

if (context.Source is GrpcHttpRequestData requestData)
{
    var sourceString = new StreamReader(requestData.Body).ReadToEnd();
    bytes = Encoding.UTF8.GetBytes(sourceString);
}

this won't work for the case HttpRequestWithPOCOAndHttpRequest / it won't set the HttpRequestData req, if that was expected behaviour.


image

image

Program.cs

using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace FunctionApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
#if DEBUG
            Debugger.Launch();
#endif

            var host = new HostBuilder()
                .ConfigureAppConfiguration(c =>
                {
                    c.AddCommandLine(args);
                })
                .ConfigureFunctionsWorker((c, b) =>
                {
                    b.UseDefaultWorkerMiddleware();
                })
                .ConfigureServices(s =>
                {
                    //s.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
                })
                .Build();

            await host.RunAsync();
        }
    }
}

Function1.cs

using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;

namespace FunctionApp
{
    public class Function1
    {
        [Function("Function1")]
        public HttpResponseData Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
            FunctionContext context)
        {
            var response = req.CreateResponse(HttpStatusCode.OK);

            response.Headers.Add("Date", "Mon, 18 Jul 2016 16:06:00 GMT");
            response.Headers.Add("Content", "Content - Type: text / html; charset = utf - 8");
            response.WriteString("Book Sent to Queue!");

            return response;
        }


        [Function(nameof(HttpRequestWithPOCOAndHttpRequest))]
        public HttpResponseData HttpRequestWithPOCOAndHttpRequest(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] Book book,
            HttpRequestData req,
            FunctionContext context)
        {
            var response = req.CreateResponse(HttpStatusCode.OK);
            response.WriteString("Book Sent to Queue!");
            return response;
        }

        [Function(nameof(HttpRequestWithPOCO))]
        public void HttpRequestWithPOCO(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] Book book,
            FunctionContext context)
        {
            
        }

        public class Book
        {
            public string name { get; set; }
            public string id { get; set; }
        }
    }
}

Requests.http

###

POST http://localhost:7071/api/Function1 HTTP/1.1
content-type: application/json

{
    "name": "sample",
    "id": "3"
}


###

POST http://localhost:7071/api/HttpRequestWithPOCO HTTP/1.1
content-type: application/json

{
    "name": "sample",
    "id": "3"
}

###

POST http://localhost:7071/api/HttpRequestWithPOCOAndHttpRequest HTTP/1.1
content-type: application/json

{
    "name": "sample",
    "id": "3"
}

FunctionApp.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <IsPackable>false</IsPackable>
    <TargetFramework>net5.0</TargetFramework>
    <LangVersion>preview</LangVersion>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    <_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
    <SignAssembly>true</SignAssembly>
    <AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>DEBUG;TRACE</DefineConstants>
  </PropertyGroup>
  <ItemGroup>
    <Compile Remove="Function2\**" />
    <Compile Remove="Function3\**" />
    <Compile Remove="Function4\**" />
    <Compile Remove="Function5\**" />
    <EmbeddedResource Remove="Function2\**" />
    <EmbeddedResource Remove="Function3\**" />
    <EmbeddedResource Remove="Function4\**" />
    <EmbeddedResource Remove="Function5\**" />
    <None Remove="Function2\**" />
    <None Remove="Function3\**" />
    <None Remove="Function4\**" />
    <None Remove="Function5\**" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.1-preview5" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\extensions\Worker.Extensions.Abstractions\Worker.Extensions.Abstractions.csproj" />
    <ProjectReference Include="..\..\extensions\Worker.Extensions.Http\Worker.Extensions.Http.csproj" />
    <ProjectReference Include="..\..\extensions\Worker.Extensions.Storage\Worker.Extensions.Storage.csproj" />
    <ProjectReference Include="..\..\src\DotNetWorker\DotNetWorker.csproj" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

@eriklieben
Copy link

eriklieben commented Mar 13, 2021

This seems to be the way if you want both the HttpRequestData and the POCO from the body.

[Function(nameof(HttpRequestWithPOCO))]
public async Task<HttpResponseData> HttpRequestWithPOCO(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
    FunctionContext context)
{
    var response = req.CreateResponse(HttpStatusCode.OK);
    var book = await req.ReadFromJsonAsync<Book>();

    response.Headers.Add("Date", "Mon, 18 Jul 2016 16:06:00 GMT");
    response.Headers.Add("Content", "Content - Type: text / html; charset = utf - 8");
    response.WriteString(JsonSerializer.Serialize(book));

    return response;
}

@swilkodev
Copy link
Author

swilkodev commented Mar 14, 2021

Thanks @eriklieben for the suggestion. I can confirm that works in my repo so I will use that technique for now.

For the response I'll also use response.WriteAsJsonAsync<T>();

@brettsam
Copy link
Member

@fabiocav -- did you have a branch with some of these changes in progress?

@mattchenderson mattchenderson added the area: http Items related to experience improvements for HTTP triggers label Jun 7, 2023
@fabiocav
Copy link
Member

This has been merged.

We're working on documentation updates and will have the package out soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: http Items related to experience improvements for HTTP triggers
Projects
None yet
6 participants