17

I am trying to make the redirect work in AWS API gateway. I configured the method response to include Location in the header and on the Integration Response, I set the parameter as : Location = integration.response.body.location. however, when I test the API, instead of redirecting me to the location, it just shows me the text for the location on the API page. Has anyone encountered this before?

The string shown is actually the correct location but the API does not redirect me to that URL.

leo c
  • 663
  • 3
  • 8
  • 19
  • Where actually you want to redirect? Is it anything from aws-lambda? – Andremoniy Jun 24 '16 at 13:12
  • @Andremoniy The initial page is from S3 which is a form. Once submitted, it calls an AWS Lambda function through API and then when the API responds, I want to redirect to another html file in the same bucket. I have isolated the problem at the moment. If I put the actual location as a string within {}, the redirect works. However, if I store the JSON object into a variable and pass that variable in the callback, the redirect does not work. – leo c Jun 25 '16 at 05:07
  • 1
    does it show any CORS issue? – holopekochan Apr 21 '20 at 06:52

8 Answers8

17

It's possible to redirect to a hardcoded URL using API Gateway without using AWS Lambda. Here's the exported OpenAPI 3.0 definition for how this is accomplished:

openapi: "3.0.0"
info:
  title: 'TheApiName'
  version: ''
paths:
  "/":
    get:
      responses:
        "302":
          description: "302 response"
          headers:
            Location:
              schema:
                type: "string"
          content: {}
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "302"
            responseParameters:
              method.response.header.Location: "'https://github.com/lukedemi/adventbot'"
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        passthroughBehavior: "when_no_match"
        type: "mock"

Note that when you import this API template you still need to "Deploy" it, and you need to create a "Stage" when doing so and then associate the API:Stage with a Custom Domain (which requires an ACM cert when first creating the custom domain in API Gateway) and then you can ONLY deploy this single API to the domain since you need to use an empty basepath (which resolves to /) and that mapping doesn't allow any other /anything routes due to the way API Gateway works.

dragon788
  • 3,583
  • 1
  • 40
  • 49
Luke
  • 329
  • 3
  • 7
  • It is good to keep in mind if you want to borrow this for existing infrastructure to check whether you are using Swagger 2.0 or OpenAPI 3.0 and convert accordingly..... – dragon788 Dec 11 '19 at 22:16
  • You can validate the syntax using https://editor.swagger.io/ which is really handy because I was getting errors trying to import it and it turned out it was missing a couple required fields (I've submitted an update request to the answer with the results). – dragon788 Dec 17 '19 at 20:55
10

There is a simpler way for Lambda redirects with API Gateway:

module.exports.handler = (event, context, callback) => Promise.resolve(event)
  // .then(doStuffWithTheEventObject)
  .then(() => callback(null, {
    statusCode: 302,
    headers: {
      Location: 'https://gohere.com',
    },
  })) // Success!
  .catch(callback); // Fail function with error

This example is from https://blog.rowanudell.com/redirects-in-serverless/

SethO
  • 2,703
  • 5
  • 28
  • 38
Tom Roggero
  • 5,777
  • 1
  • 32
  • 39
  • @user2517028 yes. I am using the code above in production. It works. Be more specific on what doesnt work if you want help. – Tom Roggero Aug 06 '18 at 04:21
  • I copied the exact same code as it is and there was no redirection towards the link specified in the "Location" header. The response received by the browser was the entire object specified in the 2nd parameter of your callback. – user2517028 Aug 06 '18 at 16:41
  • I ended up using this: https://aws.amazon.com/blogs/compute/redirection-in-a-serverless-api-with-aws-lambda-and-amazon-api-gateway/ – user2517028 Aug 06 '18 at 16:42
  • But I think it is not the perfect solution, as we can't control the response (for example add multiple headers params) – user2517028 Aug 06 '18 at 16:43
3

Here is what I actually used in my Lambda. I let the API well alone. The API collects data sent to it: form data. It is a very simple bit of code that you can do a lot with.

I process the form data in lambda: extract variables, revalidate them, save them into dynamoDB, then issue an email asking the new signup to verify their intentions to satisfy GDPR and make sure I have captured a genuine email address from someone who is capable and interested in interacting and then provide an on-screen response.

The on-screen response is a redirection to a specific thank-you page explaining they need to check their email and click on the link.

All you need to do in the example is swop Amazon.com for your landing page.

By using the variable: redirectURL I can vary the response and use the API and Lambda for a range of different forms on different website pages. I can set the redirect as a hidden form field in the form itself if I want to.

    let redirectURL= 'https://amazon.com/';
    var response = {
        "statusCode": 302,
        "headers": {
            Location: redirectURL,
        }
    };
    callback(null, response);
David White
  • 621
  • 1
  • 10
  • 23
1

Inspired by Luke's answer

AWS API Gateway natively supports Velocity (a Java-based template engine) which enables us to dynamically set the redirect URL and even set cookies based on the incoming request (headers, query params, body, etc).

Here is a 302 redirect example without the use of Lambda:

# AWS Console > API Gateway > Create API > REST API > Import > Import from Swagger or Open API 3
openapi: 3.0.1
info:
  title: 'API Gateway redirect without Lambda. Powered by API Gateway Mock Integration'
  description: 'Accept a query parameters: "url" as the destination (optional), "pid" as the affiliate partner tracking ID cookie value (optional)'
  version: ''
paths:
  "/redirect":
    get:
      parameters:
      - name: url
        in: query
        schema:
          type: string
      - name: pid
        in: query
        schema:
          type: string
      responses:
        '302':
          description: 302 response
          headers:
            Cache-Control:
              schema:
                type: string
            Set-Cookie:
              schema:
                type: string
            Content-Type:
              schema:
                type: string
            Location:
              schema:
                type: string
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: '302'
            responseParameters:
              method.response.header.Cache-Control: "'no-store, no-cache, must-revalidate'"
              method.response.header.Set-Cookie: "''"
              method.response.header.Content-Type: "'text/html'"
              method.response.header.Location: "''"
            responseTemplates:
              application/json: >
                #*
                  Velocity (a Java-based template engine) is natively supported by AWS API Gateway
                *#

                #set($domain = ".example.com")

                #set($redirectUrl = $input.params("url"))
                #if($redirectUrl != "")
                  #set($redirectUrl = $util.urlDecode($redirectUrl))
                #else
                  #set($defaultFallbackUrl = "https://www$domain")
                  #set($redirectUrl = $defaultFallbackUrl)
                #end
                #set($context.responseOverride.header.Location = $redirectUrl)

                #set($trackingCookieValue = $input.params("pid"))
                #if($trackingCookieValue != "")
                  #set($trackingCookieName = "affiliate_partner_id")
                  #set($maxAge = 60 * 60 * 24 * 30)
                  #set($context.responseOverride.header.Set-Cookie = "$trackingCookieName=$trackingCookieValue; Max-Age=$maxAge; path=/; domain=$domain")
                #end
        requestTemplates:
          application/json: '{"statusCode":200}'
        passthroughBehavior: when_no_templates
        type: mock

Appendix: if you need to deploy the API Gateway mock integration via pure CloudFormation, here is my tricky solution:

# Reference: https://cloudkatha.com/api-gateway-custom-domain-using-cloudformation/
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Demo: how to deploy API Gateway via pure CloudFormation'

Resources:
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Body: <% Content of API Gateway OpenAPI YAML %>
  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref ApiGatewayDeployment__CUSTOM_FILE_HASH_PLACEHOLDER__
      RestApiId: !Ref ApiGatewayRestApi
      StageName: !Ref Env
  # ⬇️ Solution B of https://medium.com/@rokaso/aws-cloudformation-terraform-not-deploying-your-api-gateway-changes-cd87d30850cc (Solution C does not work)
  ApiGatewayDeployment__CUSTOM_FILE_HASH_PLACEHOLDER__:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref ApiGatewayRestApi
  ApiGatewayBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      BasePath: ""
      DomainName: <% Your domain name %>
      RestApiId: !Ref ApiGatewayRestApi
      Stage: !Ref ApiGatewayStage
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
kenberkeley
  • 8,449
  • 3
  • 35
  • 25
0

Are you sure the API endpoint is returning 302?

Make sure you don't have any 2xx or 3xx response codes other than 302 defined for your method. You can only have one "successful" response code mapping. 2xx and 3xx codes are all considered success codes.

Lorenzo d
  • 2,016
  • 10
  • 17
  • Yes, I'm quite confident that I am getting a response of 302 because the string beig shown is the location where I wanted to redirect to. Looks like it's not working if I store the value into a var and then pass the variable as part of the callback. If I put it directly in the call back, it works... – leo c Jun 25 '16 at 05:10
0

Ok, I found the problem. The variable I was storing the location to is defined as a string instead of an object which is why, the web page is showing it as text. It is now redirecting to the page.

leo c
  • 663
  • 3
  • 8
  • 19
0

Firstly, you want to create a method response for the "location" header. Then you'd like to map the location header value from the response of your Lambda. In Node.js runtime you could map it to integration.response.body.errorType. This way you'll be able to redirect by doing:

// Returns 302 or 301
var err = new Error("HandlerDemo.ResponseFound Redirection: Resource found elsewhere");
err.name = "http://a-different-uri";
context.done(err, {});

For the full writeup, see: https://aws.amazon.com/blogs/compute/redirection-in-a-serverless-api-with-aws-lambda-and-amazon-api-gateway/

ronaldwidha
  • 1,345
  • 2
  • 12
  • 24
0

To redirect some result to other uri using lambda function and api gateway , you have to use

var err = new Error("HandlerDemo.ResponseFound Redirection: Resource found elsewhere"); err.name = result; context.done(err, {}); in lambda function so context it will forward the error And on api gateway `Method Response:> Add HTTP STATUS as 302 Response Headers for 302 Add Location

For Mapping Integration Response Header Mapping Location integration.response.body.errorType

This will work

credits from this url https://aws.amazon.com/blogs/compute/redirection-in-a-serverless-api-with-aws-lambda-and-amazon-api-gateway/

Mahesh Giri
  • 1,810
  • 19
  • 27