Foreword
This course, in a nutshell, is a shit show. We are being tested on the most irrelevant crap - details we don't ever need to remember as webdevs (hello MDN Web Docs) and API-specific shit from Express!
Then they expect you to be able to compile badly-written JavaScript in your head after they teach it to you in ~4 weeks. Don't even get me started on the project. The education quality here is laughable. Students will not take away any skills from this course.
Here I'll outline some important information you should know for the exams. Do note that a lot of the material is simplified as I focus on how to answer the exam, not to provide actual webdev knowledge. Please tweet any suggestions/requests to @kevincharm. You can also submit a PR on the github repo.
If anyone important is reading this; contact me. I am keen to teach CSE1500 next year (trade for ECs or waive my tuition) because students paying >EUR10k deserve better education than this.
JavaScript
JavaSkrrrt
Classes & Modules
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return this.age;
}
public void birthday() {
this.age = this.age + 1;
}
}
// Then this class can be instantiated with:
Person person = new Person("Kevin", 42);
// and you can invoke instance methods like:
person.birthday();
// and get the age like this:
int age = person.getAge();
Above is an example class in Java.
- We can't get access to
name
because it's private and has no getter!- Therefore we say that
name
is encapsulated.
- Therefore we say that
- There are multiple ways to write this class in JavaScript. Below we go through some examples of how to write this equivalent class in JavaScript.
Prototype Pattern
// This is the constructor:
function Person(name, age) {
this.name = name
this.age = age
}
// These are *instance* methods;
// meaning they have access to `this` (itself).
Person.prototype.getAge = function () {
return this.age
}
Person.prototype.birthday = function () {
this.age = this.age + 1
}
// Then this "class" can be instantiated with:
var person = new Person('Kevin', 42)
// and you can invoke instance methods like:
person.birthday()
// and get the age like this:
var age = person.getAge()
// OR
var age = person.age
// ...and this is also works!
var name = person.name
- This is called the prototype pattern.
- It does not support encapsulation, because we are able to access any of its attributes, including
name
.
Module Pattern
function Person(name, age) {
var name = name
var age = age
function getAge() {
return age
}
function birthday() {
age = age + 1
}
// Tricks to watch out for:
// Usually, with the module pattern, we don't need to use `this` as in other patterns!
// With the module pattern,
// we _explicitly return_ what we want to expose (make public):
return {
getAge: getAge,
birthday: birthday
}
}
// We create the instance like so (notice the lack of `new`):
var person = Person('Kevin', 42)
// Then we can do
person.birthday()
// and
var age = person.getAge()
// but we cannot access name! So it's encapsulated :)
- This is called the module pattern (or a factory, but I don't think we use that name in this course).
- Allows encapsulation. (
name
is not accessible; so therefore it is "private")
Event Loop
Asynchronous, Synchronous, Blocking, Non-blocking
Synchronous
- Synchronous -> blocking.
- Events will be executed one after the other, sequentially, in order.
Example with synchronous version of the readFile
function:
// Here, the program waits until the file is read.
// Then the contents of the file are assigned to the `contents` variable.
var contents = fs.readFileSync("file.txt")
// This will now print the contents of the file:
console.log(contents)
Asynchronous
- Asynchronous -> non-blocking.
- Events will happen in parallel, and the order is not guaranteed.
- Accepts callback functions that get called when the event is finished.
Example with asynchronous version of the readFile
function:
var contents
// Here, the program does NOT wait until the file is read.
// The `fs.readFile` function returns *immediately*!
// Note how `readFile` accepts a callback - this is what gets called
// when it is finished reading.
fs.readFile("file.txt", function (err, fileContents) {
// This function is run AFTER the file reading is done, some time in the future.
// We don't know when.
contents = fileContents
})
// At this point, the file reading is not done yet, so contents will still be empty!
console.log(contents)
The order in which the operations happen in this script are as follows:
- Line 30:
fs.readFile
- Line 37:
console.log
- Line 33:
contents = fileContents
Functions
Functions can be declared in 2 different ways.
Function Declaration
- Function declarations are hoisted.
- Hoisted means that it is "pulled up" to the top of the current scope.
- This means you can call it before it's declared.
sayHello('Kevin') // this works!
function sayHello(name) {
console.log('Hello, ' + name)
}
Function Expression
- Function expressions are functions which are assigned to a variable.
- They are NOT hoisted.
- This means you can not call it before it is declared.
sayHello('Kevin') // this will throw an error!
var sayHello = function (name) {
console.log('Hello' + name)
}
Objects
Accessing properties
var person1 = {
name: 'Kevin',
age: 69,
background: {
nationality: 'Australian'
}
}
// These are valid ways to access these objects' properties:
// The standard dot-notation
var name = person1.name
var age = person1.age
var nationality = person1.background.nationality
// Bracket notation
var name = person1['name']
// This is usually what it's used for:
var AGE_PROPERTY = 'age'
var age = person1[AGE_PROPERTY]
// A mix of both:
var nationality = person1['background'].nationality
Modules
This is a common trick question that appears in the exams.
Exporting a plain object
// Exporting a plain object.
// Any code that `requires` this will share the same object.
module.exports = {
name: 'Kevin',
age: 42
}
// Also valid, equivalent to above
module.exports.name = 'Kevin'
module.exports.age = 42
Exporting a function that returns an object
// Exporting a function that returns an object
// Any code that `requires` and calls this will
// get a NEW object every time.
module.exports = function () {
return {
name: 'Kevin',
age: 42
}
}
// Also watch out with this one, you need to require it like this:
var foo1 = require('foo')()
console.log(foo1.name) // prints 'Kevin'
// This is NOT valid and will return a function instead of an object
var foo2 = require('foo')
console.log(foo2.name) // undefined!
Express
Middlewares
var app = require('express')()
app.use(function (req, res, next) {
console.log('first middleware!')
// call the next middleware in the chain
next()
})
app.use(function (req, res, next) {
console.log('second middleware!')
// call the next middleware in the chain
next()
})
app.use(function (req, res, next) {
console.log('third middleware!')
// end the response
res.end()
})
The code above illustrates a middleware chain.
- Middlewares are declared by passing a callback function into
app.use
- Order of declaration of middlewares is important.
var app = require('express')()
app.get('/action', function (req, res) {
console.log('action route')
res.end()
})
app.post('/user', function (req, res) {
console.log('user route')
res.end()
})
// Now the following middleware will be called before a request
// is routed to any of the routes above.
app.use(function (req, res, next) {
console.log('first middleware!')
// call the next middleware in the chain
// in this case, it goes to either
// - the GET /action handler, or
// - the POST /user handler
next()
})
EJS vs Ajax
- EJS is a templating language used in Express.
- This allows the server to insert data dynamically into templates that are basically HTML.
- This means data can be inserted before the webpage is returned to the browser.
- Ajax is a way for webpages that are already loaded on the browser to make HTTP requests.
- This means that existing webpages can make HTTP requests, then use those responses to insert new data into itself.
- This also means more HTTP requests as the webpage first needs to be requested.
CSS
Cascading Swagsheets
Selectors
Descendant
main h1 {
background-color: red;
}
This matches any h1
element that is a child, grandchild, great grandchild, etc of any main
element.
<main>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
</main>
<main>
<article>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
</article>
</main>
Child
main > h1 {
background-color: red;
}
This matches any h1
element that is a direct child of any main
element.
<main>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
</main>
<main>
<article>
<h1>
<!-- Does not match, this h1 will not have a red background -->
Kevin loves croissants!
</h1>
</article>
</main>
Multiple Selectors
Don't get this confused with the other two. The key here is the comma.
main, h1 {
background-color: red;
}
This matches any element that is a main
or a h1
.
<main>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
</main>
<main>
<article>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
</article>
</main>
<h1>
<!-- Matches, this h1 will have a red background -->
Kevin loves croissants!
</h1>
HTTP
Hypertext Transfer Protocol. Remember that HTTP version 1.1 is still the most widely used protocol.
URL
Anatomy of a URL
<protocol>://<hostname>:<port>/<path>?<query_params>
<protocol>
required
http
https
ftp
(probably don't need to know this one, also heaps more)
<hostname>
required
localhost
google.com
tudelft.nl
cse1500.sendcroissants.me
really.long.and.nested.subdomains.co.uk
<port>
optional
80
(this is the default port when no port is specified)3000
5000
- ...and any other integer from
0
to65535
<path>
optional
login
calendar/january/31
- ...it's a path. You know what a path is.
<query_params>
optional
key=value
pairs separated by &, comes after the ?.
Usually used for sending data in a GET request since GET requests can't send content in the body.
username=kevin
username=kevin&token=fdfa8e7cc4b3
- this is basically like sending this in the body:
{ "username":"kevin", "token":"fdfa8e7cc4b3" }
- this is basically like sending this in the body:
Examples
# Ordinary URL, note the <port> is missing, so it defaults to 80
https://google.com
# Pointing to computer's own address, note the port is specified as 3000
# Also note it has a query string with some data.
http://localhost:3000/register?username=kevin&password=yeet
Status Codes
Here I'll list the important ones you should know. For everything else, there is HTTP Cats.
200
The 200s are success codes. It means your GET request was successful, or your POST request was successful and a new account was created, etc.
200 OK
201 Created
- usually after a POST
300
The 300s are all about redirection. It's still a success, just means that the resource has moved somewhere else.
301 Moved permanently
302 Found
- usually a temporary redirect
400
The 400s are about bad requests. Anything 400 or higher is an error.
400 Bad request
401 Unauthorized
- usually when you aren't logged in or you haven't supplied some Authorization header403 Forbidden
- you're logged in/authenticated, but you don't have access (maybe the route is only for admins)404 Not found
500
The 500s are about server errors.
500 Internal server error
- some error occurred on the server.
Request Methods
Don't get tricked, remember these names! Some trick names they might use are UPDATE
instead of PUT
or REMOVE
instead of DELETE
. BE CAREFUL!!!
You should always think of requests as <method> <path>
. These always go together. Example:
GET /users/42
POST /register
PUT /settings
GET
A GET request can not contain a body (you can't send some JSON with it) so it should only be used to retrieve data. HOWEVER! You can still send data in the query string. So technically a GET request can do everything the other methods can.
HEAD
A subset of GET
. The request looks the same, but in the response, you only get the headers back, with no response body.
POST
Should be used for sending data to the server, because you can attach things to the request body: JSON, a file, etc.
PUT
& PATCH
Should be used for updating data. You can attach stuff to the request body.
DELETE
Should be used for deleting data. You can attach stuff to the request body.
Security
CSRF, XSS, etc.
CSRF, XSS, etc
CSRF (Cross-site Request Forgery)
CSRF is when a malicious site uses your authenticated session to inject data to the server using your account. They key to remembering this is the word Forgery; like when you forged your mum's signature in high school so you could skip school and hang out in the city.
How to prevent CSRF attacks on your server/app
- Make sure your sessions expire.
- Reauthenticate your users often.
- Example 1: ask them to login again from time to time
- Example 2: add a CAPTCHA
XSS (Cross-site Scripting)
XSS is when a malicious site injects scripts to your server, but does not strictly need authenticated sessions.
Example: Users are able to post comments on the site. A malicious user posts a comment containing the following content:
<script>alert('lol')</script>
Once posted, the server displays the comment as it is, without sanitising it. Now when other users see this comment, they will get a rude message box saying "lol".
How to prevent XSS attacks on your server/app
- Make sure any user-inputted data is sanitised/escaped.
- When displaying user-inputted data such as comments, always sanitise inputs before rendering them.
- ELI5: Sanitising means making the code into a plain string so that it can't be executed by a browser.
Indirect Secure Object Reference
This is, for example, when you are able to manipulate a URL to get access to another user's settings page with sensitive information.
For example:
- Your user settings page is at
http://tudelft.nl/users/42/settings
- Then you try changing the URL to
http://tudelft.nl/users/69/settings
- Surprisingly, you are able to see some other person's settings page and see their personal details such as home address, phone number, etc.
- This is indirect because there is no link that lets you do this, but you have to play around with the URL.
- This is supposed to be a secure object because it contains sensitive information like home address.
- The reference to this object is the user id (42 and 69).
How to prevent Indirect Secure Object Reference access
- Protect your routes with authentication.
- Don't use sequential numbers as IDs. For example, you could use a UUID that looks like
123e4567-e89b-12d3-a456-426655440000
, which is random and unguessable.
Bonus: CSRF vs XSS
Quora: What is the difference between XSS and CSRF from their execution perspective?
DoS
DoS stands for Denial of Service. This is when an attacker prevents your server from receiving packets.
Some ways to DoS
- Overload the server with so much traffic that it stops being able to process requests.
- Block packets/requests from reaching the server.
Encoding vs Encryption
¡MUY IMPORTANTE!
Encoding is NOT the same as encryption. Don't get tricked!!!
Encryption DOES provide security.
Encoding DOES NOT provide security.
¡MUY IMPORTANTE!
Encoding
When you encode something, it is possible to decode it back easily. For example, you may encode some text containing Chinese characters into Base64 so that it can be sent over HTTP. This can then be decoded back on the other end easily. Therefore, it provides no security!
Encryption
When you encrypt something, it is either impossible to decrypt it or you will require a key to decrypt it.
Example 1: Passwords are usually encrypted with an algorithm like bcrypt, which means it cannot be decrypted back.
Example 2: When using HTTPS, data is encrypted before being sent. There is a public key and a private key. The private key is used to encrypt the data, then the public key can be used to decrypt the data.