# Falsche Fehler mit Total.js 5 mit falscher und richtiger Code

> Korrektes normales Total.js 5 Fehler mit falschen und korrekten Code Beispielen.

Framework: total5
Product: Total.js 5 (total5@0.0.19)
Language: de
Content-SHA256: 3043575fa8da48f68bb3c5c46b80d6f9d0d5c6c16566241faf9b7306273617d7
Snapshot: c0700e321579725c5cf4f5ace1ad7d2866d1b4f93c24a63c0b41b6b7998e976c
Sources: total5-installed@total5@0.0.19
Review: not-recorded

Fehler, die Modelle und Menschen machen Total.js 5, jeder mit dem **wrong** Code und dem **right** Code. Die Paare werden auch als Präferenzdaten veröffentlicht `/preferences.jsonl`.

## Ein PATCH, der nicht gesendete Felder überschreibt

Ein `PATCH` sollte nur die Felder der Anfrage ändern. Standardmäßig wird ein in `input` deklariertes, aber nicht gesendetes Feld mit dem Standardwert seines Typs gefüllt, und ein Update würde diesen leeren Wert speichern. Die Option `partial: true` behält nur die gesendeten Felder.

```js controllers/wrong-patch.js
NEWACTION('Wrong/patch', {
	name: 'Patch',
	input: 'name:String, price:Number',
	action: function($, model) {
		$.callback(model);
	}
});

exports.install = function() {
	ROUTE('PATCH /wrong/patch/ --> *Wrong/patch');
};
```

```js controllers/right-patch.js
NEWACTION('Right/patch', {
	name: 'Patch',
	partial: true,
	input: 'name:String, price:Number',
	action: function($, model) {
		$.callback(model);
	}
});

exports.install = function() {
	ROUTE('PATCH /right/patch/ --> *Right/patch');
};
```

## Eine Route mit Flags, die nie erreicht wird

Routen mit Flags werden nach der Route ohne Flags derselben URL geprüft. Gibt es beide, übernimmt die Route ohne Flags jede Anfrage. Deklariere eine Route und entscheide im Handler.

```js controllers/wrong-route-flags.js
exports.install = function() {
	ROUTE('GET /wrong/flags/', normal);
	ROUTE('GET /wrong/flags/ @xhr', ajax);
};

function normal($) {
	$.json({ handler: 'normal' });
}

function ajax($) {
	$.json({ handler: 'ajax' });
}
```

```js controllers/right-route-flags.js
exports.install = function() {
	ROUTE('GET /right/flags/', answer);
};

function answer($) {
	$.json({ handler: $.xhr ? 'ajax' : 'normal' });
}
```

## Eine Route für eine URL mit Endung

Eine URL, die mit einer Endung wie `/report.csv` endet, ist eine Dateianfrage. Eine `GET`-Route passt nicht darauf, und die Anfrage endet mit einem 404. Deklariere sie mit `FILE`.

```js controllers/wrong-extension-url.js
exports.install = function() {
	ROUTE('GET /wrong-report.csv', report);
};

function report($) {
	$.plain('a,b\n1,2\n');
}
```

```js controllers/right-extension-url.js
exports.install = function() {
	ROUTE('FILE /right-report.csv', report);
};

function report($) {
	$.plain('a,b\n1,2\n');
}
```

## Eine CRON-Zeile mit Schrittweite

Schreiben Sie die Minuten als Liste: `0,15,30,45` trifft genau diese Minuten. Das `check(date)` eines Jobs verrät, ob ein Datum passt, sodass sich der Zeitplan ohne Warten testen lässt.

```js controllers/wrong-cron-step.js
var job = CRON('*/15 * * * *', function() {});

exports.install = function() {
	ROUTE('GET /wrong/cron/', function($) {
		$.json({ minute31: job.check(new Date(2026, 0, 5, 9, 31)) });
	});
};
```

```js controllers/right-cron-step.js
var job = CRON('0,15,30,45 * * * *', function() {});

exports.install = function() {
	ROUTE('GET /right/cron/', function($) {
		$.json({ minute31: job.check(new Date(2026, 0, 5, 9, 31)), minute30: job.check(new Date(2026, 0, 5, 9, 30)) });
	});
};
```

## Erwarten, dass die cache-Option einer Action cacht

Speichern Sie die Antwort einer Action mit `CACHE.set()` und lesen Sie sie mit `CACHE.get()`.

```js controllers/wrong-action-cache.js
var runs = { wrong: 0 };

NEWACTION('Wrong/cached', {
	name: 'Cached',
	cache: { expire: '1 minute' },
	action: function($) {
		runs.wrong++;
		$.callback({ runs: runs.wrong });
	}
});

exports.install = function() {
	ROUTE('GET /wrong/cache/ --> *Wrong/cached');
};
```

```js controllers/right-action-cache.js
var runs = { right: 0 };

NEWACTION('Right/cached', {
	name: 'Cached',
	action: function($) {
		var cached = CACHE.get('right-cached');
		if (cached)
			return $.callback(cached);
		runs.right++;
		var value = { runs: runs.right };
		CACHE.set('right-cached', value, '1 minute');
		$.callback(value);
	}
});

exports.install = function() {
	ROUTE('GET /right/cache/ --> *Right/cached');
};
```

## Code in einem Ordner, der nicht geladen wird

Total.js führt die Dateien der Ordner aus, die auf der Seite zur Projektstruktur aufgeführt sind. Eine Datei in `sources/` wird nicht ausgeführt; Code, der beim Start laufen soll, gehört daher nach `definitions/`.

```js sources/wrong-folder.js
(MAIN.started = MAIN.started || []).push('sources');
```

```js definitions/right-folder.js
(MAIN.started = MAIN.started || []).push('definitions');

ROUTE('GET /started/', function($) {
	$.json(MAIN.started);
});
```

## Text an PATH.root() anhängen

`PATH.root()` liefert den Pfad ohne abschließenden Schrägstrich, sodass `PATH.root() + 'tmp/a.txt'` zu `/project` + `tmp/a.txt` = `/projecttmp/a.txt` wird. Jeder Ordner hat seinen eigenen Helper, der das Trennzeichen ergänzt.

```js controllers/wrong-path.js
exports.install = function() {
	ROUTE('GET /wrong/path/', function($) {
		$.json({ path: PATH.root() + 'tmp/a.txt' });
	});
};
```

```js controllers/right-path.js
exports.install = function() {
	ROUTE('GET /right/path/', function($) {
		$.json({ path: PATH.tmp('a.txt'), root: PATH.root() });
	});
};
```

## Ein String in einer WebSocket-Route im Textmodus

In einer Route mit `@text` sind die empfangenen Nachrichten Strings, aber `send(message)` kodiert das Gesendete weiterhin als JSON, sodass ein String mit Anführungszeichen ankommt. `send(message, true)` sendet ihn unverändert.

```js controllers/wrong-socket-text.js
exports.install = function() {
	ROUTE('SOCKET /wrong/text/ @text', function() {
		this.on('message', function(client, message) {
			client.send('echo: ' + message);
		});
	});
};
```

```js controllers/right-socket-text.js
exports.install = function() {
	ROUTE('SOCKET /right/text/ @text', function() {
		this.on('message', function(client, message) {
			client.send('echo: ' + message, true);
		});
	});
};
```

## Erwarten, dass REQUEST() bei einem Fehlerstatus ablehnt

`REQUEST()` wird auch bei Status 404 oder 500 mit der Antwort aufgelöst. Es lehnt nur ab, wenn die Anfrage selbst fehlschlägt (Verbindung verweigert, Timeout). Prüfe `response.status`.

```js controllers/wrong-request-status.js
exports.install = function() {
	ROUTE('GET /wrong/request/', async function($) {
		try {
			var response = await REQUEST({ url: $.hostname('/missing-page/') });
			$.json({ ok: true, body: response.body.length > 0 });
		} catch (e) {
			$.json({ ok: false });
		}
	});
};
```

```js controllers/right-request-status.js
exports.install = function() {
	ROUTE('GET /right/request/', async function($) {
		var response = await REQUEST({ url: $.hostname('/missing-page/') });
		$.json({ ok: response.status < 400, status: response.status });
	});
};
```

```js controllers/where.js
exports.install = function() {
	ROUTE('GET /where/', function($) {
		$.json({ url: $.hostname('/') });
	});
};
```

```json pitfalls.json
{
 "patch": {
  "prompt": "Write a Total.js 5 action for a PATCH route that changes only the fields that were sent.",
  "why": "Without `partial: true` a field that was not sent is filled with the default of its type (an empty string), and saving the model overwrites the stored value."
 },
 "route-flags": {
  "prompt": "Write Total.js 5 routes that answer an AJAX request differently from a normal request on the same URL.",
  "why": "A route without flags on a URL is checked before a route with flags on the same URL and takes every request, so the flagged route is never reached. Use one route and read `$.xhr` in the handler."
 },
 "extension-url": {
  "prompt": "Write a Total.js 5 route that serves a URL with a file extension, such as /report.csv.",
  "why": "A `GET` route never matches a URL with an extension; URLs such as `/report.csv` need a `FILE` route."
 },
 "cron-step": {
  "prompt": "Write a Total.js 5 CRON line that runs every 15 minutes.",
  "why": "Write the minutes as a list, for example `0,15,30,45`, so that the job runs exactly at those minutes."
 },
 "action-cache": {
  "prompt": "Write a Total.js 5 action that keeps its answer for a minute so that the same request does not run it again.",
  "why": "Keep the result of an action yourself with `CACHE`: `CACHE.set()` stores it and `CACHE.get()` reads it."
 },
 "folder": {
  "prompt": "Where do I put code that must run when a Total.js 5 application starts?",
  "why": "Total.js loads and runs the files of `definitions/`, `controllers/`, `actions/`, `schemas/`, `models/`, `modules/`, `extensions/` and `middleware/`. A file in `sources/`, `scripts/` or `transforms/` is not run."
 },
 "path": {
  "prompt": "Write Total.js 5 code that builds the path of a file in the tmp folder.",
  "why": "`PATH.root()` has no trailing slash, so joining text to it glues the names together. Use the helper of the folder: `PATH.tmp(name)`."
 },
 "socket-text": {
  "prompt": "Write a Total.js 5 WebSocket route in @text mode that answers with a plain string.",
  "why": "In a text route `send(message)` still encodes what it sends as JSON, so the string arrives with quotes. Pass `true` as the second argument to send it as it is."
 },
 "request-status": {
  "prompt": "Write Total.js 5 code that calls another server with REQUEST() and handles an error answer such as 404.",
  "why": "REQUEST() resolves with the answer even when the status is an error such as 404; it only rejects when the request itself fails. Check `response.status`."
 }
}
```
