- Dart:Scalable Application Development
- Davy Mitchell Sergey Akopkokhyants Ivo Balbaert
- 1130字
- 2021-07-09 18:56:23
A blog editor
A blog will have an administration section that will allow us to update the blog using the Web. This will be accessible through the address http://localhost:8080/admin
, using the admin
login as the username and Password1
as the password (both case sensitive).
The admin section of the blog's website will consist of web forms. The forms will use the post
method so that the blog server will have to detect this correctly in the _handleRequest
method of the BlogServerApp
class to handle the request. This is exposed by the method
property of the request
object, as shown in the following code snippet:
if (request.method == 'POST') { _handleFormPost(request); }
POST
requests still have a URI associated with them, allowing a decision over how to handle the input. For detailed information, let's take a look at the following code snippet:
void _handleFormPost(HttpRequest request) { if (request.uri.path == "/login") _performLogin(request); if (request.uri.path == "/add") _performNewPost(request); }
The handling of the login process and the new blog post are rather different.
Password protection
The /admin
path serves the web form that allows the entry of the username and password required to access the admin features, as shown in the following screenshot:

The username and password will be verified, and if they are correct, the form to add a blog post will be shown to you. Let's take a look at the following code snippet:
void _performLogin(HttpRequest request) { request.listen((List<int> buffer) { String page = _checkAdminLogin(buffer); request.response ..statusCode = HttpStatus.OK ..headers.set('Content-Type', 'text/html') ..write(page) ..close(); }, onDone: () => request.response.close()); }
The checkAdminLogin
method performs the checking of the login details and returns either the web form or a message stating that the login attempt has failed.
Encryption
Security is an important feature of any administration feature of a web application. We will not want to store the password on the site in plain text! A typical way to store the password is a one-way hash, which makes the password hard to crack even if the attacker has the hash value.
The crypto
package provides a range of encryption options. The SHA
algorithm will be used for this purpose for the blog, as follows:
String _checkAdminLogin(List<int> buffer) { var sha = new SHA256(); sha.add(buffer); var digest = sha.close(); String hex = CryptoUtils.bytesToHex(digest); String page = ""; if (hex != expectedHash) { page = wrongPassword; } else { page = addForm; } return page; }
Conveniently, the input for the hash is a List<int>
list, and this is exactly what is received from the web form input into the request's listen
handler. The SHA256
algorithm simply takes a body of data and then has the close
method called to return the calculated hash.
As both the username and password have to be correct, the entire input from the form can simply be hashed and compared against a previously calculated value. For easy storage, the hash is converted to a hexadecimal string, as follows:
const String expectedHash = "bdf252c8385e7c4ae76bca280acd8d44f85d7fdb56f1bfb44f5749ff549ba2f6";
Handling more complex forms
Of course, form handling is not always as straightforward as the login page. Typically, form elements need to be split apart and handled separately. The served page after logging into the administration page will allow the user to add a new post to the blog. The user will supply the post title and body text and the post ID number and date will be generated by the application, as shown in the next screenshot:

Processing the form
The _performNewPost
method calls the getFormData
utility function to split the data into a more manageable list. This rather low-level web programming is typically wrapped up by most Dart web frameworks, as shown in the following code snippet:
List _getFormData(List<int> buffer) { var encodedData = new String.fromCharCodes(buffer); List pieces = encodedData.split("&"); List data = []; List finalData = []; pieces .forEach((dateItem) => data.add(dateItem.substring(dateItem.indexOf("=") + 1))); data.forEach( (encodedItem) => finalData.add(Uri.decodeQueryComponent(encodedItem))); return finalData; }
The data is first split into data from each control, and then the data is decoded using the Uri.decodeQueryComponent
component to the raw data before it is returned to the caller.
Saving data to a disk
The data has been processed to a manageable list, so now we only need to construct a blog post and save it to the filesystem. This is carried out by the _performNewPost
method of the BlogServerApp
method. Let's take a look at the following code snippet:
List formData = _getFormData(buffer); String newID = hostedBlog.getNextPostID(); String filename = "$newID.txt"; var p = path.join("content", "posts"); p = path.join(p, filename); File postFile = new File(p); String post = BlogPost.createBlogPost(formData[0], formData[1]); postFile.writeAsStringSync(post);
The blog
object returns the next free PostId
, which is used in the file name, and the full path is constructed using path.join
from the path
package. The createBlogPost
static method on the BlogPost
class is used to add extra data in the defined format. This is written to the constructed path using the synchronous write function.
This version of the add form does not allow a user to set a graphic file, which each post requires. If the blog post ID is 8
, then 8.png
will be requested. Rather than not loading any file, a default image (default.png
) will be loaded up. Let's take a look at the following screenshot:

If the file is not found on the filesystem (such as the fictitious 1234.png
image!), then 6.png
is loaded and served instead, as shown in the following code snippet:
File getBlogImage(int index) { String path; if (imgPaths.containsKey(index)) { path = imgPaths[index]; } else { path = _pathDefaultImg; } return new File(path); }
This is implemented by simply testing the imgPaths
map for the requested index
variable. If the key is not present in the imgPaths
map, the default image path is used.
The content of the blog is read when the blog server is started. As this content has now changed with the new blog post, this list must be refreshed using the BlogServerApp
method called initBlog
, as follows:
initBlog() async { _cache = {}; IDs = new List<int>(); postPaths = new Map<int, String>(); imgPaths = new Map<int, String>(); await _loadPostList(); _loadImgList(); }
The BlogServerApp
and _performNewPost
methods perform the task of calling initBlog
before serving the new post, as follows:
hostedBlog.initBlog(); req.response ..statusCode = HttpStatus.OK ..headers.set('Content-Type', 'text/html') ..redirect(new Uri(path: "post$newID.html")) ..close();
Once the blog is reinitialized, the application serves the new blog post to the user as a single page.
- Learning Cython Programming
- Python 3.7網(wǎng)絡(luò)爬蟲快速入門
- JavaScript高效圖形編程
- Learning Bayesian Models with R
- 編寫高質(zhì)量代碼:改善C程序代碼的125個(gè)建議
- Mastering LibGDX Game Development
- 精通Scrapy網(wǎng)絡(luò)爬蟲
- RabbitMQ Essentials
- Python圖形化編程(微課版)
- AutoCAD 2009實(shí)訓(xùn)指導(dǎo)
- 深度探索Go語(yǔ)言:對(duì)象模型與runtime的原理特性及應(yīng)用
- Android Development Tools for Eclipse
- Python計(jì)算機(jī)視覺與深度學(xué)習(xí)實(shí)戰(zhàn)
- JBoss AS 7 Development
- C語(yǔ)言程序設(shè)計(jì)實(shí)驗(yàn)指導(dǎo)與習(xí)題精解