cors-middleware
HTTP cross-origin resource sharing(CORS) middleware.
Compliant with Fetch living standard, 3.2. CORS protocol.
Terms
- 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
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: OriginCORS 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: OriginAllowCredentials
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: OriginExposeHeaders
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: OriginCORS options serialization error
Each 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>"] }));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-headersIf 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-headersAllowHeaders
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-headersMaxAge
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-headersStatus
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
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 }));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
