Why can't I mix HTTPRequest with other parameters in an EK9 service operation?

← Web Services · Ref: Q1328

When a service operation takes the full request via :=: REQUEST (an HTTPRequest), that parameter must be the ONLY parameter on the operation. Adding any second parameter — a CONTENT binding, a PATH segment, anything — triggers E07780 (SERVICE_REQUEST_BY_ITSELF).

The rule exists because HTTPRequest already gives you the entire request — path segments, query string, headers and body. Mixing it with individually-bound parameters duplicates the binding and creates two sources of truth for the same data.

CORRECT PATTERN

  process() as POST for :/process
    -> request as HTTPRequest :=: REQUEST

Read the body, headers and query values from the request inside the method body via request.content() and friends.

INCORRECT PATTERN

  process() as POST for :/process
    ->
      request as HTTPRequest :=: REQUEST
      body as String :=: CONTENT

The extra body parameter alongside the full request raises E07780.

See Q202 for parameter binding. See Q868 for valid path-parameter types.

Example

defines module qa.webdeep.service.request.only

  defines constant

    JSON_RESPONSE <- "application/json"

  defines service

    <?-
      Operation that binds the FULL request via :=: REQUEST.
      HTTPRequest is the only parameter, so E07780 is not raised.
      Any path/query/body value is read from the request inside the body.
    -?>
    ItemService :/items open

      process() as POST for :/process
        -> request as HTTPRequest :=: REQUEST
        <- response as HTTPResponse: (request: request) with trait HTTPResponse
          override content()
            <- rtn as String: `{"received": "${request.content()}"}`
          override status() as pure
            <- rtn as Integer: 201
          override contentType() as pure
            <- rtn as String: JSON_RESPONSE
          override cacheControl() as pure
            <- rtn as String: "no-store"
          override contentLanguage() as pure
            <- rtn as String: "en"
          default operator ?

  defines application

    ItemApp
      register ItemService()

  defines program

    HttpRequestOnlyDemo()
      stdout <- Stdout()
      stdout.println("HTTPRequest must be the ONLY parameter on an operation.")
      stdout.println("Read path/query/body values from the request inside the body.")

Common mistakes

E07780 — An operation that binds the full request with :=: REQUEST must have HTTPRequest as its ONLY parameter. The extra CONTENT-bound body parameter makes the operation take more than one parameter, so E07780 fires. Drop the extra parameter and read the body from the request inside the method body via request.content(). See ek9 -h E07780 for details.

Incorrect:

      process() as POST for :/process
        ->
          request as HTTPRequest :=: REQUEST
          body as String :=: CONTENT

Correct:

      process() as POST for :/process
        -> request as HTTPRequest :=: REQUEST
Other ways to ask this
  • What triggers E07780 SERVICE_REQUEST_BY_ITSELF?
  • Why must HTTPRequest be the only parameter on a service operation?
  • How do I get a path or query value when I also need the full HTTPRequest?

Coming from another language?

Java Spring: a handler may freely mix HttpServletRequest with @PathVariable/@RequestParam — nothing prevents the duplication. C# ASP.NET: HttpContext plus bound parameters coexist. Go net/http: you read *http.Request and also parse mux.Vars(r) together. EK9: the compiler forbids the mixture at compile time — if you take the full HTTPRequest you take only that and extract the rest from it.

Keywords: HTTPRequest, binding, service, rest, E07780, REQUEST, parameter, http