javascriptazureazure-functionsazure-durable-functions

Azure Durable Functions v4 HTTP Trigger Request Object Different from v3


When using Azure Durable Functions v3, the HTTP Trigger request object would already be pre-filled with the input field with headers, method, and url. There is also a body field. However, when switching to v4, the input field is blank (besides the values I myself put in there) and there is no body field. Because of this, I cannot use the data that is in the body field or values that would be in the header.

Here's an example from v3

 "input": {
    "method": "POST",
    "url": "http://localhost:7071/api/orchestrators/analyze",
    "originalUrl": "http://localhost:7071/api/orchestrators/analyze",
    "headers": {
      "accept": "*/*",
      "connection": "keep-alive",
      "host": "localhost:7071",
...


And here's from v4:

"input": {
    "query": {

    },
    "params": {
      "opName": "analyze"
    }
  },

I tried seeing if there are any discrepancies between the old trigger from v3 and the new one I'm using for v4. Besides the obvious change in structure I cannot see what could be causing the difference.

Here's the old trigger from v3:

const df = require("durable-functions")


module.exports = async function (context, req) {
    const client = df.getClient(context)
    const instanceId = await client.startNew("Orchestrator", undefined, req)

    context.log(`Started orchestration with ID = '${instanceId}'.`)

    return client.createCheckStatusResponse(context.bindingData.req, instanceId)
}

As well as its function.json:

  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "route": "orchestrators/{opName}",
      "methods": [
        "post"
      ]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "name": "starter",
      "type": "orchestrationClient",
      "direction": "in"
    }
  ]
}

v4 trigger:

const { app } = require('@azure/functions');
const df = require('durable-functions');

app.http('HttpTrigger', {
    authLevel: "anonymous",
    methods: ['POST'],
    route: 'orchestrators/{opName}',
    extraInputs: [df.input.durableClient()],
    handler: async (request, context) => {
        const client = df.getClient(context);
        const instanceId = await client.startNew("Orchestrator", {input: request});

        context.log(`Started orchestration with ID = '${instanceId}'.`);

        return client.createCheckStatusResponse(request, instanceId);
    },
});

Solution

  • Yes Indeed, V4 model does have different configuration than V3 model. You will see V4 model is no longer using function.json file alike V3. So you need to pass the request data explicitly as an input in your function.

    I have used below code to pass the a request body, method, Url and method as input to the function and getting expected response.

    const { app } = require('@azure/functions');
    const df = require('durable-functions');
    
    const activityName = 'durableHello1';
    
    df.app.orchestration('durableHello1Orchestrator', function* (context) {
        const input = context.df.getInput();
        const outputs = [];
        outputs.push(yield context.df.callActivity(activityName, input));
    
        return outputs;
    });
    
    df.app.activity(activityName, {
        handler: (input) => {
      
            const { body, headers, url, method } = input;
            return `Hello, you sent a ${method} request to ${url} with body ${JSON.stringify(body)} and headers ${JSON.stringify(headers)}`;
        },
    });
    
    app.http('durableHello1HttpStart', {
        route: 'orchestrators/{orchestratorName}',
        extraInputs: [df.input.durableClient()],
        handler: async (request, context) => {
            const client = df.getClient(context);
            
            const body = await request.json(); 
            const headers = request.headers; 
            const url = request.url; 
            const method = request.method; 
    
            const headersObj = {};
            for (const [key, value] of headers.entries()) {
                headersObj[key] = value;
            }
    
            const data = {
                body,
                headers: headersObj,
                url,
                method
            };
    
            const instanceId = await client.startNew(request.params.orchestratorName, { input: data });
            context.log(`Started orchestration with ID = '${instanceId}'.`);
            return client.createCheckStatusResponse(request, instanceId);
        },
    });
    

    Output-

    enter image description here