ForgeΤεκμηρίωση
el

Τεκμηρίωση

Συχνές σφάλματα με το Total.js 5, με λάθος και σωστό κώδικα

Total.js 5 · total5@0.0.19 guidemistakespitfalls Εκτελέστηκε με το Total.js 5

Τα λάθη που κάνουν οι άνθρωποι και τα μοντέλα Total.js 5, καθένα με τον κώδικα wrong και τον κωδικό right. τα ζευγάρια δημοσιεύονται επίσης ως προτιμήσεις σε /preferences.jsonl.

Ένα PATCH που αντικαθιστά τα πεδία που δεν στάλθηκαν

Ένα PATCH πρέπει να αλλάζει μόνο τα πεδία του αιτήματος. Από προεπιλογή, ένα πεδίο που δηλώνεται στο input αλλά δεν στάλθηκε γεμίζει με την προεπιλεγμένη τιμή του τύπου του, και μια ενημέρωση θα αποθήκευε αυτή την κενή τιμή. Η επιλογή partial: true κρατά μόνο τα πεδία που στάλθηκαν.

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');
};
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');
};

Μια διαδρομή με σημαίες που δεν φτάνει ποτέ

Οι διαδρομές με σημαίες ελέγχονται μετά τη διαδρομή χωρίς σημαίες στο ίδιο URL. Αν υπάρχουν και οι δύο, η διαδρομή χωρίς σημαίες παίρνει κάθε αίτημα. Δηλώστε μία διαδρομή και αποφασίστε στον χειριστή.

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' });
}
controllers/right-route-flags.js
exports.install = function() {
	ROUTE('GET /right/flags/', answer);
};

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

Μια διαδρομή για URL με επέκταση

Ένα URL που τελειώνει με επέκταση, όπως το /report.csv, είναι αίτημα αρχείου. Μια διαδρομή GET δεν το ταιριάζει και το αίτημα τελειώνει με 404. Δηλώστε το με FILE.

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

function report($) {
	$.plain('a,b\n1,2\n');
}
controllers/right-extension-url.js
exports.install = function() {
	ROUTE('FILE /right-report.csv', report);
};

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

Μια γραμμή CRON με βήμα

Γράψτε τα λεπτά ως λίστα: το 0,15,30,45 ταιριάζει ακριβώς με αυτά τα λεπτά. Το check(date) μιας εργασίας λέει αν μια ημερομηνία ταιριάζει, οπότε το πρόγραμμα μπορεί να δοκιμαστεί χωρίς αναμονή.

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)) });
	});
};
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)) });
	});
};

Να περιμένετε ότι η επιλογή cache μιας ενέργειας κάνει προσωρινή αποθήκευση

Κρατήστε την απάντηση μιας ενέργειας με το CACHE.set() και διαβάστε την με το CACHE.get().

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');
};
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');
};

Κώδικας σε φάκελο που δεν φορτώνεται

Το Total.js εκτελεί τα αρχεία των φακέλων που αναφέρονται στη σελίδα για τη δομή του έργου. Ένα αρχείο στο sources/ δεν εκτελείται, οπότε ο κώδικας που πρέπει να τρέξει στην εκκίνηση ανήκει στο definitions/.

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

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

Συνένωση κειμένου με το PATH.root()

Το PATH.root() επιστρέφει τη διαδρομή χωρίς τελική κάθετο, οπότε το PATH.root() + 'tmp/a.txt' γίνεται /project + tmp/a.txt = /projecttmp/a.txt. Κάθε φάκελος έχει τη δική του βοηθητική συνάρτηση που προσθέτει τον διαχωριστή.

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

Μια συμβολοσειρά που στέλνεται σε διαδρομή WebSocket σε λειτουργία κειμένου

Σε μια διαδρομή με @text τα μηνύματα που λαμβάνετε είναι συμβολοσειρές, αλλά το send(message) εξακολουθεί να κωδικοποιεί ό,τι στέλνει ως JSON, οπότε μια συμβολοσειρά φτάνει με εισαγωγικά. Το send(message, true) τη στέλνει ως έχει.

controllers/wrong-socket-text.js
exports.install = function() {
	ROUTE('SOCKET /wrong/text/ @text', function() {
		this.on('message', function(client, message) {
			client.send('echo: ' + message);
		});
	});
};
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);
		});
	});
};

Να περιμένετε ότι το REQUEST() απορρίπτει σε κατάσταση σφάλματος

Το REQUEST() επιλύεται με την απάντηση ακόμη και όταν η κατάσταση είναι 404 ή 500. Απορρίπτει μόνο όταν αποτυγχάνει το ίδιο το αίτημα (άρνηση σύνδεσης, χρονικό όριο). Ελέγξτε το response.status.

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 });
		}
	});
};
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 });
	});
};
controllers/where.js
exports.install = function() {
	ROUTE('GET /where/', function($) {
		$.json({ url: $.hostname('/') });
	});
};
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`."
 }
}