HTTP cross-origin resource sharing(CORS) middleware.
Compliant with Fetch living standard, 3.2. CORS protocol.
- CORS request
- A request with
Origin
header - CORS preflight request
- A CORS request and satisfies the following:
- Method is
Options
- Includes
Access-Control-Request-Method
header - Includes
Access-Control-Request-Headers
header
- Method is
For a definition of Universal HTTP middleware, see the http-middleware project.
Add a CORS header to the response in the downstream.
No special action is taken in response to CORS preflight requests. Use preflight for that.
import {
cors,
type Handler,
} from "https://deno.land/x/cors_middleware@$VERSION/mod.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";
const middleware = cors();
const corsRequest = new Request("test:", {
headers: { origin: "<origin>" },
});
declare const handler: Handler;
const response = await middleware(corsRequest, handler);
assert(response.headers.has("access-control-allow-origin"));
yield:
Access-Control-Allow-Origin: *
Vary: Origin
cors
accept following options:
Name | Type | Description |
---|---|---|
allowOrigins | * | (string | RegExp )[] |
Allowed origin list. |
allowCredentials | true | "true" |
Access-Control-Allow-Credentials |
exposeHeaders | string[] |
Access-Control-Expose-Headers |
allowOrigins
is *
or a list of allowed origins.
The default is *
.
If *
, Access-Control-Allow-Origin
(*) will add to the response.
The list may consist of strings or regular expression objects.
The middleware compares Origin
header and each element of the allowOrigins
.
If matched, Access-Control-Allow-Origin
(Origin header) will add to the
response.
If no match, Access-Control-Allow-Origin
(null) will add to the response.
import {
cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";
const middleware = cors({
allowOrigins: ["https://test.example", /^https:\/\/cdn\..*/],
});
yield:
Access-Control-Allow-Origin: <Origin>
Vary: Origin
The allowCredentials
value will serialize and added to the response as
Access-Control-Allow-Credentials
header.
import {
cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
const middleware = cors({ allowCredentials: true });
yield:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Vary: Origin
The value of exposeHeaders
will serialize and added to the response as an
Access-Control-Expose-Headers
header.
However, if the request is a CORS preflight request, it is not added.
import {
cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
const middleware = cors({ exposeHeaders: ["x-test"] });
yield:
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: x-test
Vary: Origin
Each option will serialize.
If serialization fails, it throws an error as follows:
- Elements of
exposeHeaders
are not<field-name
format
import {
cors,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assertThrows } from "https://deno.land/std/testing/asserts.ts";
assertThrows(() => cors({ exposeHeaders: ["<invalid:field-name>"] }));
Middleware will make changes to the following elements of the HTTP message.
- HTTP Headers
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
- Vary
Create CORS preflight request handler.
import {
type Handler,
preflight,
} from "https://deno.land/x/cors_middleware@$VERSION/mod.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";
import { assertSpyCalls, spy } from "https://deno.land/std/testing/mock.ts";
const corsPreflightRequest = new Request("test:", {
method: "OPTIONS",
headers: {
origin: "<origin>",
"access-control-request-method": "POST",
"access-control-request-headers": "content-type",
},
});
declare const handler: Handler;
const next = spy(handler);
const handlePreflight = preflight();
const response = await handlePreflight(corsPreflightRequest, next);
assertSpyCalls(next, 0);
assert(response.status === 204);
assert(response.headers.has("access-control-allow-origin"));
assert(response.headers.has("access-control-allow-methods"));
assert(response.headers.has("access-control-allow-headers"));
assert(response.headers.has("vary"));
yield:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: content-type
Vary: origin, access-control-request-method, access-control-request-headers
If the request is not a CORS preflight request, next
will execute.
preflight
accept following options:
Name | Type | Description |
---|---|---|
allowMethods | string[] |
Access-Control-Allow-Methods |
allowHeaders | string[] |
Access-Control-Allow-Headers |
maxAge | number |
Access-Control-Max-Age |
status | 200 | 204 |
Preflight response status code. |
and CORS request options without exposeHeaders
.
The value of allowMethods
will serialize and added to the response as an
Access-Control-Allow-Methods
header.
If not specified, Access-Control-Request-Method
header will add as
Access-Control-Allow-Methods
header to the response.
import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
const handler = preflight({ allowMethods: ["POST", "PUT"] });
yield:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT
Access-Control-Allow-Headers: <Access-Control-Request-Headers>
Vary: origin, access-control-request-method, access-control-request-headers
The value of allowHeaders
will serialize and added to the response as an
Access-Control-Allow-Headers
header.
If not specified, Access-Control-Request-Headers
will add as
Access-Control-Allow-Headers
header to the response.
import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
const handler = preflight({ allowHeaders: ["x-test1", "x-test2"] });
yield:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: <Access-Control-Request-Method>
Access-Control-Allow-Headers: x-test1, x-test2
Vary: origin, access-control-request-method, access-control-request-headers
The value of maxAge
will serialize and added to the response as an
Access-Control-Max-Age
header.
import { preflight } from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
const handler = preflight({ maxAge: 86400 });
yield:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: <Access-Control-Request-Method>
Access-Control-Allow-Headers: <Access-Control-Request-Headers>
Access-Control-Max-Age: 86400
Vary: origin, access-control-request-method, access-control-request-headers
The default is 204 No Content.
You can change to 200 OK.
Throws an error if option has an invalid value.
This is following case:
- Elements of
allowMethods
are not<method>
format - Elements of
allowHeaders
are not<field-name>
format maxAge
is not non-negative integer
import {
preflight,
} from "https://deno.land/x/cors_middleware@$VERSION/middleware.ts";
import { assertThrows } from "https://deno.land/std/testing/asserts.ts";
assertThrows(() => preflight({ allowMethods: ["<invalid:method>"] }));
assertThrows(() => preflight({ allowHeaders: ["<invalid:field-name>"] }));
assertThrows(() => preflight({ maxAge: NaN }));
All APIs can be found in the deno doc.
Why are there two separate modules?
Because the two offer different functions. cors creates middleware to provide CORS headers.
On the other hand, preflight creates a handler for CORS preflight requests. (Although it is actually a middleware signature, since it transfers processing to subsequent requests other than CORS preflight requests.)
Mixing middleware with handler characteristics and middleware characteristics will create expressive constraints.
Copyright © 2023-present httpland.
Released under the MIT license