HTTP cross-origin resource sharing(CORS) middleware.
Compliant with Fetch living standard, 3.2. CORS protocol.
- CORS request
- A request with
Originheader - CORS preflight request
- A CORS request and satisfies the following:
- Method is
Options - Includes
Access-Control-Request-Methodheader - Includes
Access-Control-Request-Headersheader
- 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: Origincors 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: OriginThe 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: OriginThe 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: OriginEach option will serialize.
If serialization fails, it throws an error as follows:
- Elements of
exposeHeadersare not<field-nameformat
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-headersIf 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-headersThe 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-headersThe 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-headersThe 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
allowMethodsare not<method>format - Elements of
allowHeadersare not<field-name>format maxAgeis 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
