1

when i add new function using func new --name MyHttpTrigger --template "HttpTrigger" there is no function.json created in this function, and when i tried to add it to current directory and run func start --build, i get this error :

No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

you can find here my function.json content :

{
  "disabled": false,
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "res",
      "type": "http",
      "direction": "out"
    }
  ]
}

Pervious httpTrigger function

namespace final
{
    public static class httpTrigger
    {
        [FunctionName("httpTrigger")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }
    }
}

New httpTrigger function

namespace final
{
    public static class httpTrigger
    {
        public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }
    }
}

Community
  • 1
  • 1
Ryuuk
  • 89
  • 1
  • 3
  • 14
  • 2
    C# (n.b. not C# script) functions should have their function.json files generated by the build process, so you shouldn't have to add one manually. Can you clarify where you're adding the function.json file (is it in the same directory as your host.json? elsewhere?), and when? (Pre-build? Do you not see a function.json within /bin/output/MyHttpTrigger/ post-build and are adding it there?) – Katy Shimizu Feb 01 '19 at 02:30
  • 1
    when i create function with C# using `func new` without --csx argument, and i tried to add function near to function.json near to `host.json`, also i tried to add new directory to this function and add function.json near to `run.cs` (because no directory is created for the function by default), but when i use --csx argument, new directory is created with function.json ... – Ryuuk Feb 01 '19 at 08:55
  • Thanks for this information. Replying as an answer. – Katy Shimizu Feb 02 '19 at 00:28

1 Answers1

6

Short Answer The build process takes care of generating function.json files for the functions you define in your .cs files based on the attributes you attach to your methods. There are several examples in the Functions docs, ex. a BlobTrigger function in C#. You shouldn't need to add your own function.json.

See the Long Answer section for a detailed breakdown of how this works behind the scenes. If you see all the expected build outputs described for a compiled C# function and the functions runtime still isn't locating any functions, check that you're running func start --build against the top-level directory of your function app structure.

Long Answer The behavior you're describing is by design. It sounds like you're used to the folder structure of Functions used by scripting languages, such as for .csx (C# script) files. Here's an example that defines two functions, MyFunction and MySecondFunction:

FunctionApp
| - bin
| - MyFunction
| | - function.json
| | - run.csx
| - MySecondFunction
| | - function.json
| | - run.csx
| ...
| host.json
| local.settings.json

Originally, the Functions runtime only recognized this folder structure, and C# functions could only be written in C# script. (original blog announcement | MSDN magazine article) Later, regular C# support was added. I'll refer to regular C# as compiled C# to emphasize the distinction between it and C# script.

The same example as a compiled C# function app has a folder structure like this:

FunctionApp
| - bin
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs

If you build this project and expand the FunctionApp/bin folder, you'll see something like this:

FunctionApp
| - bin
| | - Debug
| | | - net461
| | | | - bin
| | | | - MyFunction
| | | | | - function.json
| | | | - MySecondFunction
| | | | | - function.json
| | | - host.json
| | | - local.settings.json
| | | - netstandard2.0
| | | | - …
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs

(The netstandard2.0 folder will contain similar content as the net461 folder; they're just different framework build targets.)

Note that the similarity between FunctionApp/bin/Debug/net461 in the compiled C# function app's folder structure andFunctionAppin the C# script app's folder structure. This is because the build process for C# (not C# script) function apps uses the attributes of the methods in the .cs files (ex.HttpTrigger`) to determine what functions have been defined and creates the original folder structure as its build output.

When the Azure Functions runtime is started (ex. by func host start), it doesn't look at FunctionApp to figure out what functions exist and wire up bindings. It looks at FunctionApp/bin/Debug/net461/MyFunction.

The only difference is found in the folders for each function. In the compiled C# function app, each function's folder lacks the .csx file in the C# script function app. Take a closer look at the function.json in the compiled C# function app's FunctionApp/bin/Debug/net461/MyFunction folder and you'll see something like this:

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.13",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "post"
      ],
      "authLevel": "function",
      "name": "req"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/VSSample.dll",
  "entryPoint": "VSSample.HttpStart.Run"
}

Compared to the function.json created for a C# script function, the compiled C# function's function.json has a few extra fields. Here's what each indicates to the functions runtime:

  • generatedBy: this function.json was generated during compilation, not written by hand
  • configurationSource: this function.json was generated from C# attributes
  • scriptFile: the location of the DLL containing the compiled code corresponding to this function, relative to this function.json file's location
  • entryPoint: the method signature of the function within the compiled DLL

Long story short, a compiled C# function app relies on the same folder structure as a C# script function app at runtime, but it's generated by the build process instead of being constructed by the developer. A developer writing a function app in compiled C# defines their bindings through C# attributes, leaving function.json generation to the build process.

Katy Shimizu
  • 1,051
  • 5
  • 9