@httpland/cors-middleware v1.0.0
cors-middleware
HTTP cross-origin resource sharing(CORS) middleware.
Compliant with Fetch living standard, 3.2. CORS protocol.
Terms
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
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
3 years ago
3 years ago
