serverless / serverless

⚡ Serverless Framework – Use AWS Lambda and other managed cloud services to build apps that auto-scale, cost nothing when idle, and boast radically low maintenance.

Home Page:https://serverless.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Response template does not allow for multiple content-types

edfrench opened this issue · comments

This is a Feature Proposal

Description

APIG selects the proper response content type based on the request Accepts Header.
From AWS APIG documentation:

Select Mapping Templates

The request mapping template used to transform the method request body into the integration request body is selected by the value of the "Content-Type" header sent in the client request.

The response mapping template used to transform the integration response body into the method response body is selected by the value of the "Accept" header sent in the client request.

For example, if the client sends headers of "Content-Type : application/xml", and "Accept : application/json", the request template with the application/xml key will be used for the integration request, and the response template with the application/json key will be used for the method response.

Only the MIME type is used from the Accept and Content-Type headers when selecting a mapping template. For example, a header of "Content-Type: application/json; charset=UTF-8" will have a request template with the application/json key selected.

We have several API's that rely on this behavior to allow users to select the response's content-type The current design of serverless response template only allows a single content-type to be defined, rather than allowing for multiple content types.

response:
    contentType: text/html
    template:
        - "#set($inputRoot = $input.path('$'))"
        - "$inputRoot.html"

If the response template was patterned after the request templates, users of a service could determine the content-type of the response using the standard Accepts header mechanism.

          request:
            template:
              text/xhtml: { "stage" : "$context.stage" }
              application/json: { "httpMethod" : "$context.httpMethod" }

Specific Use Cases

There are two specific use cases we have for this functionality:

  1. Our configuration service allows a client to determine which format it wants in the response. Each request Accepts header maps directly to the equivalent content-type:
    Accepts: application/json
    Accepts: application/xml
    Accepts: application/x-sh
  2. Most of our services use the quasi-standard mechanism of using the request Accepts header to select a version of our API. This is the same mechanism Github and other sites use to select different versions of an API (Github API Versioning)
    Accepts: application/vnd.shelfbucks.v5+json
    Accepts: application/vnd.shelfbucks.v6+json
    Accepts: application/vnd.shelfbucks.v7+json
    This use case results in the responses all mapping to same content-type (application/json) but with different json schemas.

Similar or dependent issues:

Additional Data

  • _Serverless Framework Version 1.0.0-rc.1_:
  • _Operating System_:
  • _Stack Trace_:
  • _Provider Error messages_:

@edfrench just a note that there's a typo in your title of this issue "response"

I have some other issues with the way event mapping is handled for responses. I'm not sure if they should be part of this issue or separate, but they're related enough that I'll start by putting them here. Currently:

  1. you can not map your own error responses - your own error response regex, status code, or templates
  2. you can not change the status code of your default response

For bullet (1) I have the need to define my own response templates for error codes. For instance, I want a 404 to look like this:

{
   "message": "Not Found",
   "status": 404
}

In my Lambda function I return a stringifed JSON object. Then in SLS 0.5.x I used the following templates:

   "standardApiResponses": {
      "400": {
         "statusCode": "400"
      },
      "404": {
         "statusCode": "404",
         "selectionPattern": ".*\\\"status\\\":404.*",
         "responseParameters": "$${errorResponseParameters}",
         "responseModels": {
            "application/json;charset=UTF-8": "Empty"
         },
         "responseTemplates": {
            "application/json;charset=UTF-8": "$input.path('$.errorMessage')"
         }
      },
      "500": {
         "statusCode": "500",
         "selectionPattern": ".*\\\"status\\\":500.*",
         "responseParameters": "$${errorResponseParameters}",
         "responseModels": {
            "application/json;charset=UTF-8": "Empty"
         },
         "responseTemplates": {
            "application/json;charset=UTF-8": "$input.path('$.errorMessage')"
         }
      },
      "default": {
         "statusCode": "200",
         "responseParameters": "$${defaultResponseParameters}",
         "responseModels": {
            "application/json;charset=UTF-8": "Empty"
         },
         "responseTemplates": {
            "application/json;charset=UTF-8": "$input.path('$.body')"
         }
      }
   },

For bullet (2), I have an API endpoint that needs to return a 302 response code - imagine you're building tinyurl.com - it's the same principle. But I can't override the status code in SLS 1.0. In SLS 0.5.x I did this:

{
   "standardApiResponses": {
      "default": {
         "statusCode": "302",
         "responseParameters": {
            "method.response.header.Location": "integration.response.body.headers.Location"
         },
         "responseTemplates": {
            "application/json;charset=UTF-8": "$input.path('$.body')"
         }
      }
   }
}

Another usecase for the request that @edfrench makes came to me just now - as you can see in my templates from 0.5.x above, I had application/json;charset=UTF-8 as my mime-type. That's important because browsers and other clients handle it a lot better when they know the charset. But right now the default is application/json and I have no way to override it.

@edfrench by the way, I didn't want my migration to SLS 1.0 to be stopped, so today I whipped up a plugin to allow this to work: https://github.com/silvermine/serverless-plugin-multiple-responses

I'll add details to the README.md file ASAP

README updated - better to do it now than put it off. Hope that helps. It allowed me to keep moving with my upgrade. Gotta say, the SLS plugin philosophy is very helpful in that regard - I was able to override the default behavior that way.

Awesome for building this plugin @jthomerson !

We're currently working on a new APIG implementation that should resolve a lot, if not all of those issues. We'll announce soon (can't sadly tell more at this point)

@flomotlik so this means this new APIG interface will be different from @jthomerson plugin, right?

@filipedeschamps The new APIG interface is the proxy one. #2185 It is very different to the existing lambda integration.

Closing as this can be solved with APIG Proxy integration (suggested solution) or by using mentioned plugin.