Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returning html always return a HTTP 200 status code #56

Open
pnwatin opened this issue Nov 18, 2023 · 4 comments · May be fixed by #87
Open

Returning html always return a HTTP 200 status code #56

pnwatin opened this issue Nov 18, 2023 · 4 comments · May be fixed by #87

Comments

@pnwatin
Copy link

pnwatin commented Nov 18, 2023

When using this plugin the handler will always return HTTP 200 code even if status code is explicitly set

reproduction

export const app = new Elysia()
	.use(html())
	.get('/should-return-error', ({set}) => {
		set.status = 422;

		return <div></div>;
	})

it returns http code 200 instead of 422.

Most of the time it's fine. But sometimes (for example using htmx) you want to be able to return html with your error

@eduwr
Copy link

eduwr commented Jan 17, 2024

I think I found the issue, the response will always return with status 200 here

return new Response(

And it is not updating the status received on the set property before returning the response here

if (response instanceof Response) return response

@jonkarrer
Copy link

I will take a shot at this one 🏀

snorremd added a commit to snorremd/elysia-html that referenced this issue Mar 30, 2024
Fix elysiajs#56

Previously the html plugin would always return 200 for HTML responses.
This commit adds support for reading the status code in set.status to
determine which status code to use. It fallbacks to 200 if no status
code is set. It uses the StatusMap exported by Elysia to map res.status
values supplied as string literals.

Example for statuscode has been added to examples folder showcasing
returning Forbidden status. Added test for overriding status code.

Needed to upgrade Elysia peer and dev dependency to a newer version as
the StatusMap value was not available in elysia 1.0.2. Running bun
install and pnpm install also updated the minor versions of @kitajs/*
dependencies.
@snorremd snorremd linked a pull request Mar 30, 2024 that will close this issue
snorremd added a commit to snorremd/elysia-html that referenced this issue Mar 30, 2024
Fix elysiajs#56

Previously the html plugin would always return 200 for HTML responses.
This commit adds support for reading the status code in set.status to
determine which status code to use. It fallbacks to 200 if no status
code is set. It uses the StatusMap exported by Elysia to map res.status
values supplied as string literals.

Example for statuscode has been added to examples folder showcasing
returning Forbidden status. Added test for overriding status code.

Needed to upgrade Elysia peer and dev dependency to a newer version as
the StatusMap value was not available in elysia 1.0.2. Running bun
install and pnpm install also updated the patch versions of @kitajs/*
dependencies.
@hverlin
Copy link

hverlin commented Apr 7, 2024

According to this code, using cookies or any other headers will not work as well @SaltyAom

const response = await handleHtml(

@snorremd
Copy link

snorremd commented Jul 4, 2024

I ended up just making my own plugin/middleware to handle HTML rendered using JSX with Kita HTML. You don't need much as Kita by default always renders to a string. I ended up grabbing the is html-check from the Elysia HTML plugin, but handle returning HTML as suggested in one of the documentation pages. Just transform with onAfterHandle and add the one necessary content header. If you need streaming support you probably want to use the official HTML plugin though.

In tsconfig compilerOptions:

{
    "jsx": "react-jsx",
    "jsxImportSource": "@kitajs/html",
    "plugins": [{ "name": "@kitajs/ts-html-plugin" }],
}

The custom plugin module:

import { Elysia } from "elysia";

/**
 * A super fast and simple way to check if a string is HTML
 * Check for opening and closing <tags> and if they are the same
 * @param str the response
 */
export function isHTML(str: unknown): str is string {
	if (typeof str !== "string") return false;
	const trimmed = str.trim();

	return (
		trimmed.length >= 7 &&
		trimmed[0] === "<" &&
		trimmed[trimmed.length - 1] === ">"
	);
}

/**
 * Adds an onAfterHandle hook to the app that checks if the response is HTML.
 * If it is, it sets the content-type header to text/html and adds a doctype.
 * We only ever render HTML to string, so we don't need to care about streaming.
 * @returns
 */
export const htmlPlugin = () => {
	const app = new Elysia({ name: "html" })
		.derive({ as: "global" }, () => {})
		.onAfterHandle({ as: "global" }, ({ response, set }) => {
			if (isHTML(response)) {
				set.headers["content-type"] = "text/html; charset=utf-8";
				return `<!doctype html>${response}`;
			}
		});

	return app;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants