leo c
leo c

Reputation: 693

redirecting from aws api gateway using response 302

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.

Upvotes: 18

Views: 40760

Answers (8)

kenberkeley
kenberkeley

Reputation: 9836

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

Upvotes: 1

Mahesh Giri
Mahesh Giri

Reputation: 1840

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/

Upvotes: 0

tomasdev
tomasdev

Reputation: 5996

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/

Upvotes: 10

Luke
Luke

Reputation: 349

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.

Upvotes: 19

David White
David White

Reputation: 656

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);

Upvotes: 3

ronaldwidha
ronaldwidha

Reputation: 1345

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/

Upvotes: 0

leo c
leo c

Reputation: 693

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.

Upvotes: 0

Lorenzo d
Lorenzo d

Reputation: 2066

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.

Upvotes: 0

Related Questions