You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Functions 4.0+TypeScript+gRPC下未检测到作业函数问题排查

Azure Functions + TypeScript 提示“No job functions found”问题排查与解决

问题概述

基于Node.js和TypeScript开发的Azure Function应用,集成了gRPC客户端,目录结构为MyGrpcFunctionApp -> src -> functions -> index.ts,已配置function.json且部署无报错,但执行func start --verbose时提示“No job functions found”,访问http://localhost:7071/api/httpTrigger无响应。

解决步骤

1. 修正函数导出方式与类型定义

问题核心在于:自定义Context类型覆盖了官方类型,导致运行时无法识别函数签名;同时默认导出的函数形式不符合Azure Functions运行时的识别规则。

修改后的TypeScript代码:

// 恢复导入官方类型,替换自定义Context
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import path from 'path';

const PROTO_PATH = path.join(__dirname, '..', 'proto', 'helloworld.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const helloworld = protoDescriptor.helloworld as any;

const client = new helloworld.Greeter('localhost:50051', grpc.credentials.createInsecure());

// 使用官方Context类型,保持标准函数签名
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    
    let body: any;

    // 处理请求体逻辑
    if (req.body instanceof ReadableStream) {
        const reader = req.body.getReader();
        const decoder = new TextDecoder();
        let result = '';
        let done = false;

        while (!done) {
            const { value, done: streamDone } = await reader.read();
            done = streamDone;
            if (value) {
                result += decoder.decode(value, { stream: true });
            }
        }

        try {
            body = JSON.parse(result);
            context.log('Parsed body:', body);
        } catch (error) {
            context.res = {
                status: 400,
                body: "Invalid JSON body"
            };
            return;
        }
    } else {
        body = req.body;
    }

    const name = req.query.name || (body ? body.name : null);

    if (name) {
        context.res = {
            status: 200,
            body: `Hello ${name}`
        };
    } else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

// 使用命名导出,匹配function.json中的entryPoint
export { httpTrigger };

2. 验证function.json配置

确保scriptFile路径指向编译后的正确文件,且entryPoint与函数命名导出一致:

{
    "bindings": [
        {
            "authLevel": "function",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": ["get", "post"]
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ],
    "scriptFile": "../dist/src/functions/index.js",
    "entryPoint": "httpTrigger"
}

3. 检查TypeScript编译配置

确认tsconfig.json的编译输出路径与function.json匹配:

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "CommonJS",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}

执行npm run build编译代码,确认dist目录生成正确的文件结构。

4. 清理缓存并重启函数宿主

执行以下命令彻底清理缓存并重启:

func stop
rm -rf .azurefunctions
npm run build
func start --verbose

生效原因

  • Azure Functions运行时优先识别命名导出的函数(而非默认导出),尤其是CommonJS模块环境下。
  • 使用官方Context类型确保函数签名与运行时预期完全匹配,避免类型不匹配导致的函数识别失败。
  • 正确的编译配置保证生成的.js文件路径与function.json中的scriptFile一致,运行时能找到函数入口。

内容的提问来源于stack exchange,提问作者Website Tests

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:37:37