Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

CORS and OpenWhisk Web Actions

DZone's Guide to

CORS and OpenWhisk Web Actions

In this post we take a look at how to handle CORS with OpenWhisk. Read on for the details and some code!

· Web Dev Zone ·
Free Resource

Code something amazing with the IBM library of open source blockchain patterns. Content provided by IBM.

By default, OpenWhisk will handle the relevant responses for CORS.

Specifically, it will respond to an OPTION request with these headers:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH
Access-Control-Allow-Headers: Authorization, Content-Type

If you need to change what is sent or you don't want to send these headers at all, then you need to do is set the annotation web-custom-options to true and handle the OPTIONS header yourself.

Note that if you don't set this annotation, you must not set any of these headers yourself as you'll break things!

Implementing the CORS Headers Yourself

To implement the CORS headers yourself you do something like this in your action (example in Swift, PHP version at the end):

func main(args: [String:Any]) -> [String:Any] {

let corsAllowedOrigin = "http://example.com" // URI that may access this resource ("*" is wildcard)

    // OPTIONS handling for CORS
    guard
        let method = args["__ow_method"] as? String,
        let headers = args["__ow_headers"] as? [String:Any]
    else {
        return ["statusCode": 500]
    }
    if method == "options" {
        let allowedHeaders = headers["access-control-request-headers"] ?? "Content-Type, Authorization"
        return [
            "statusCode": 200,
            "headers": [
                "Access-Control-Allow-Methods": "OPTIONS, GET, PUT, DELETE",
                "Access-Control-Allow-Origin": corsAllowedOrigin,
                "Access-Control-Allow-Headers": allowedHeaders
            ]
        ]
    }

    let data = ["hello": "world"]

    return [
        "statusCode": 200,
        "headers": [
             "Content-Type": "application/json",
             "Access-Control-Allow-Origin": corsAllowedOrigin, // don't forget this header!
         ],
        "body": Data(WhiskJsonUtils.dictionaryToJsonString(jsonDict: data)!.utf8).base64EncodedString()
    ]
}

Create it in OpenWhisk with:

wsk action create api api.swift --web true -a web-custom-options true

For a "non-simple" request, the browser will send an OPTIONS request to the option that you need to respond to. This is called the "preflight" request.

The __ow_method in args will tell you the method (lowercased) and __ow_headers will contain all the headers that were sent.

If the method is options, then we need to return the CORS headers. You can expect that the client will send anAccess-Control-Request-Headers header containing the list of headers that it's going to send to you when it does the actual request. If you don't have a whitelist of headers that you're expecting, then you should send back the list you are sent, as, otherwise, the request will fail.

You also need to set the Access-Control-Allow-Methodsheader to the list the methods you accept for this endpoint; it is the CORS version of the Allow header. The client should send you an Access-Control-Request-Method header to tell you the method it will use when it sends the actual request.

If the method is not OPTIONS, then you send back your response as usual. In addition, you need to send the Access-Control-Allow-Origin header with the same value as you used for the preflight request.

That's it.

Aside: PHP version

As an aside, here's how it looks in PHP, where the type handling is looser:

<?php

function main(array $args) : array
{
    $corsAllowedOrigin = "http://example.com"; // URI that may access this resource ("*" is wildcard)

    // OPTIONS handling for CORS
    $method = $args["__ow_method"] ?? "";
    $headers = $args["__ow_headers"] ?? [];
    if ($method == "options") {
        $allowedHeaders = $headers["access-control-request-headers"] ?? "Content-Type, Authorization";
        return [
            "statusCode" => 200,
            "headers" => [
                "Access-Control-Allow-Methods" => "OPTIONS, GET, PUT, DELETE",
                "Access-Control-Allow-Origin" => $corsAllowedOrigin,
                "Access-Control-Allow-Headers" => $allowedHeaders
            ]
        ];
    }

    $data = ["hello" => "world"];

    return [
        "statusCode" => 200,
        "headers" => [
             "Content-Type" => "application/json",
             "Access-Control-Allow-Origin" => $corsAllowedOrigin, // don't forget this header!
         ],
        "body" => base64_encode(json_encode($data))
    ];
}

The PHP version operates in the same manner as the Swift one, so the discussion above applies to this code too. The most obvious differences are the lack of a guard section do handle the type and optional casting from the args dictionary and the ease of converting an array to a base64 string in PHP.

Start coding something amazing with our library of open source Cloud code patterns. Content provided by IBM.

Topics:
openwhisk ,cors ,web dev ,http headers

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}