cors-middleware
HTTP cross-origin resource sharing(CORS) middleware.
Compliant with Fetch living standard, 3.2. CORS protocol.
Terms
- 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
Middleware
For a definition of Universal HTTP middleware, see the http-middleware project.
CORS request
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 request options
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
allowOrigins
is *
or a list of allowed origins.
The default is *
.
Asterisk
If *
, Access-Control-Allow-Origin
(*) will add to the response.
List
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
AllowCredentials
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
ExposeHeaders
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
CORS options serialization error
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>"] }));
Effects
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
CORS preflight request
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.
CORS preflight options
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
.
AllowMethods
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
AllowHeaders
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
MaxAge
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
Status
The default is 204 No Content.
You can change to 200 OK.
Serialization error
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 }));
API
All APIs can be found in the deno doc.
FAQ
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.
License
Copyright © 2023-present httpland.
Released under the MIT license