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

Exception: Error: Unexpected request.body type: undefined #255

Open
Vikas252 opened this issue Oct 31, 2022 · 7 comments
Open

Exception: Error: Unexpected request.body type: undefined #255

Vikas252 opened this issue Oct 31, 2022 · 7 comments

Comments

@Vikas252
Copy link

With the same code of the issue https://github.com/dougmoscrop/serverless-http/issues/252 I'm getting an request.body error which says undefined.

The context parameter log:

bindings: {
req: {
       method: 'GET',
       url: 'http://localhost:7071/api/serverless',
       originalUrl: 'http://localhost:7071/api/serverless',
       headers: [Object],
       query: {},
       params: {},
       body: undefined,
       rawBody: undefined
     }

the only one api im using:

router.get("/api/serverless", (req, res) => {
  res.json("hi")
  console.log("hi");
})

the error:

[2022-10-31T07:15:51.445Z] Executed 'Functions.api' (Failed, Id=b1dad52a-5405-4022-bb81-b8be504b7aab, Duration=152ms)
[2022-10-31T07:15:51.445Z] System.Private.CoreLib: Exception while executing function: Functions.api. System.Private.CoreLib: Result: Failure
[2022-10-31T07:15:51.446Z] Exception: Error: Unexpected request.body type: undefined
[2022-10-31T07:15:51.446Z] Stack: Error: Unexpected request.body type: undefined
[2022-10-31T07:15:51.446Z]     at requestBody (/workspaces/serverless/node_modules/serverless-http/lib/provider/azure/create-request.js:25:11)
[2022-10-31T07:15:51.446Z]     at module.exports (/workspaces/serverless/node_modules/serverless-http/lib/provider/azure/create-request.js:32:18)
[2022-10-31T07:15:51.446Z]     at /workspaces/serverless/node_modules/serverless-http/lib/provider/azure/index.js:8:25
[2022-10-31T07:15:51.446Z]     at Object.module.exports.handlertest (/workspaces/serverless/src/handlers/hello.js:47:25)
[2022-10-31T07:15:51.447Z]     at msg (/workspaces/serverless/node_modules/azure-functions-core-tools/bin/workers/node/dist/src/worker-bundle.js:17323:26)
[2022-10-31T07:15:51.447Z]     at WorkerChannel.invocationRequest (/workspaces/serverless/node_modules/azure-functions-core-tools/bin/workers/node/dist/src/worker-bundle.js:42009:28)
[2022-10-31T07:15:51.447Z]     at ClientDuplexStream.<anonymous> (/workspaces/serverless/node_modules/azure-functions-core-tools/bin/workers/node/dist/src/worker-bundle.js:41779:30)
[2022-10-31T07:15:51.447Z]     at ClientDuplexStream.emit (events.js:400:28)
[2022-10-31T07:15:51.447Z]     at addChunk (internal/streams/readable.js:293:12)
[2022-10-31T07:15:51.447Z]     at readableAddChunk (internal/streams/readable.js:267:9).

I'm also trying to implement and debug as the azure is untested

Extra added line to debug in hello.js :

router.use(compression())
router.use(cors())
router.use(bodyParser.json())
router.use(bodyParser.urlencoded({ extended: true }))

this didnt resolve the issue

@Vikas252
Copy link
Author

Vikas252 commented Oct 31, 2022

Update: Its weird but when i send a raw data to the above api im getting the response hi

2022-10-31

i had changed the serverless.yml in this case to:

service: serverless
provider:
  name: azure
  region: West US 2
  runtime: nodejs14
  environment:
    VARIABLE_FOO: foo
plugins:
  - serverless-azure-functions
package:
  patterns:
    - "!local.settings.json"
    - "!.vscode/**"
functions:
  serverless:
    handler: src/handlers/hello.handlertest
    events:
      - http: 
          path: /
          method: ANY
          cors: true
      - http: 
          path: /{any+}
          method: ANY
          cors: true

it was only working with /api/serverless any other routes its showing 404 not found

is it broken or im doing it wrong

@TaylorBeeston
Copy link

I was able to get past this like this:

req.rawBody = req.rawBody || '';

context.res = await handler(context, req);

Also: In the serverless.yaml, you'll want to change path to route and (I think) get rid of that + after any!

@Vikas252
Copy link
Author

Vikas252 commented Nov 10, 2022

Thank you for the response @TaylorBeeston

After updating the code now its showing 204 No Content in the postman, and no response is been shown.

and had to update the serverless.yml to this

service: serverless
provider:
  name: azure
  region: West US 2
  runtime: nodejs14
  environment:
    VARIABLE_FOO: foo
plugins:
  - serverless-azure-functions
package:
  patterns:
    - "!local.settings.json"
    - "!.vscode/**"
functions:
  api:
    handler: dist/handler.handler
    events:
      - http: ANY /
        name: res
        route: '{*segments}'
        authLevel: anonymous
      - http: 'ANY {proxy}'
        x-azure-settings:
          direction: out
          name: $return

because i was getting 404 error in the old yml file any help is appreciated been stuck here since forever. Researching

@TaylorBeeston
Copy link

Okay, I've done a bunch of work myself getting this to run, and I've finally just gotten it working correctly both offline and deployed!

To start, I've started using ESBuild to drastically lower the cycle time when deploying. I did that by first adding esbuild:

pnpm i -D esbuild rimraf # You don't have to use pnpm, but I highly recommend it!

Then adding a build script:

// esbuild.mjs
import { build } from 'esbuild';

const startTime = Date.now();

console.log('🎁 Building main bundle...');

const finalBuildObj = {
    entryPoints: ['src/index.ts'], // add whatever src files your handler is in!
    platform: 'node',
    bundle: true,
    format: 'cjs',
    outfile: 'dist/index.js',
    target: 'node12',
    plugins: [],
    external: [],
    minify: true,
};

if (process.env.NODE_ENV !== 'production') {
    finalBuildObj.sourcemap = 'inline';
    finalBuildObj.minify = false;
}

build(finalBuildObj).then(() => {
    console.log(`🎁 Done building main bundle! (${Date.now() - startTime}ms)`);
});

Then with that in place, I wrapped the sls commands to add a build step first:

// package.json
  "scripts": {
    "test": "echo \"No tests yet...\"",
    "build": "rimraf dist && node esbuild.mjs",
    "sls-deploy": "pnpm build && sls deploy",
    "sls-offline": "pnpm build && sls offline",
    "start": "pnpm sls-offline"
  },

(Again, you don't have to use pnpm. Just replace pnpm with npm run in the scripts)

With this in place, you'll use pnpm start or pnpm sls-offline to start offline, and pnpm sls-deploy to deploy.

The last thing to do is update your serverless file to only include the built code:

# serverless.yaml
package:
  patterns:
    - "!**"
    - dist/**
    - app/**
    - host.json
    - local.settings.json

This change brought my "minimal" azure.zip from ~500MB down to 704KB! It also allows you to write your function in TS if that's your thing.

One more thing: You'll now be using the built js file inside of dist and not the source file inside of src, so you'll need to update your function in your serverless.yaml to point to that:

# serverless.yaml
functions:
  app:
    handler: dist/index.app

If that part is confusing to you, I can elaborate by showing you my directory structure, as well as what gets put inside of dist for me!


Okay, with ESBuild out of the way, it should be much easier for you to start testing a config that works for you. I found that adding the manual x-azure-settings event was giving me 204s locally, just like you, so I axed it. I also found that if I tried changing the routePrefix in host.json, it would break when deploying, so make sure that file just looks like this:

// host.json
{
    "version": "2.0"
}

So, in the end, my serverless.yaml file looks like this (comments stripped)

service: azure

frameworkVersion: '3'

provider:
  name: azure
  region: West US 2
  runtime: nodejs12

plugins:
  - serverless-azure-functions

package:
  patterns:
    - "!**"
    - dist/**
    - app/**
    - host.json
    - local.settings.json

functions:
  app:
    handler: dist/index.app
    events:
      - http: true
        route: "{test}"
        method: ANY
        cors: true
        authLevel: anonymous

I have a simple express app that exposes /api/hello, /api/nice, and /api/:test, echoing different things for each route, and they all work successfully for me! Hope this helps you solve your problem!

@TaylorBeeston
Copy link

Just to be extra helpful, I decided to upload my code here!

@Vikas252
Copy link
Author

Thank you for the response and for a working example @TaylorBeeston currently i have changed the folder structure a little bit and goes like app.ts has server initialization with express handler.ts has the azure serverless-http then routes.ts has all the routes when i implemented the same as you said with all the configs i'm getting can not GET /api/sls

app.ts

"use strict"
//const serverless = require('serverless-http')
import express from "express"
//import compression from "compression"
import cors from "cors"
import bodyParser from "body-parser"

import setRoutes from "./routes"

const app = express()

app.use(cors())
app.set("port", 3000)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*")
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  )
  next()
})

async function main(): Promise<any> {
  try {
    setTimeout(() => {
      setRoutes(app)
      app.listen(app.get("port"), () => {
        console.log(`listening on port ${app.get("port")}`)
      })
      console.log("Testing")
    }, 30000)
  } catch (err) {
    console.error(err)
  }
}
main()

export { app, express }

handler.ts

import serverless from 'serverless-http';
//import { app } from "./app";

import express, { Router, json } from "express"

const app = express()

const router = Router();

router.get("/hello", (req, res) => {
    console.log("You are there");
    return res.status(200).send("Nice!");
});

const handler = serverless(app,{provider:'azure'})
module.exports.app = async (context, req) => {
  req.rawBody = req.rawBody || '';
  context.res = await handler(context, req);
}

routes.ts:

//import { express } from "./app"

import express from "express"
import {app} from "./app"

function setRoutes(app): void {
  const router = express.Router()
  router.route("/serverless").get((req, res) => {
    return res.json("hi")
  })

  router.route("/sls").get((req, res) => {
    return res.json("bye")
  })

  app.use("/api", router)
}

export default setRoutes

not able to hit either hello in the handler.ts or routes in routes.ts

meanwhile i'll be also trying to rectify what silly mistake i would had done

@Vikas252
Copy link
Author

Vikas252 commented Nov 11, 2022

Update: The routes are working in postman but not in normal URL's

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants