{"id":310495,"date":"2023-01-03T04:29:47","date_gmt":"2023-01-03T03:29:47","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=go&#038;p=310495"},"modified":"2023-01-03T04:29:51","modified_gmt":"2023-01-03T03:29:51","slug":"build-a-blog-with-go-templates","status":"publish","type":"go","link":"https:\/\/blog.jetbrains.com\/zh-hans\/go\/2023\/01\/03\/build-a-blog-with-go-templates\/","title":{"rendered":"\u4f7f\u7528 Go \u6a21\u677f\u6784\u5efa\u535a\u5ba2"},"content":{"rendered":"<p><em>\u672c\u6587\u7531\u5916\u90e8\u8d21\u732e\u8005\u64b0\u5199\u3002<\/em><\/p>\n<div class=\"about-author \">\n<div class=\"about-author__box\">\n<div class=\"row\">\n<div class=\"about-author__box-img\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/aniket.jpeg\" alt=\"\" \/><\/div>\n<div class=\"about-author__box-text\">\n<h4>Aniket Bhattacharyea<\/h4>\n<p><span style=\"font-weight: 400;\">\u70ed\u7231\u8ba1\u7b97\u673a\u548c\u8f6f\u4ef6\u7684\u6570\u5b66\u7814\u7a76\u751f\u3002<\/span><\/p>\n<p><a href=\"https:\/\/abhattacharyea.dev\/\" target=\"_blank\" rel=\"nofollow noopener\"><span style=\"font-weight: 400;\">\u7f51\u7ad9<\/span><\/a><\/p>\n<p>\u00a0<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p><a href=\"https:\/\/pkg.go.dev\/text\/template\" target=\"_blank\" rel=\"noreferrer noopener\">Go \u6a21\u677f<\/a>\u529f\u80fd\u5f3a\u5927\uff0c\u53ef\u4ee5\u6839\u636e Go \u7a0b\u5e8f\u4e2d\u7684\u6570\u636e\u751f\u6210\u6587\u672c\u6216 HTML \u8f93\u51fa\u3002 \u60a8\u53ef\u4ee5\u5c06\u5bf9\u8c61\u4f20\u9012\u7ed9\u6a21\u677f\uff0c\u81ea\u5b9a\u4e49\u6570\u636e\u7684\u663e\u793a\u65b9\u5f0f\u3002 \u6a21\u677f\u901a\u5e38\u7528\u4e8e\u751f\u6210\u7f51\u9875\u3001\u7535\u5b50\u90ae\u4ef6\u548c\u5176\u4ed6\u57fa\u4e8e\u6587\u672c\u7684\u8f93\u51fa\u3002 Go \u6a21\u677f\u975e\u5e38\u6d41\u884c\u7684\u73b0\u5b9e\u7528\u6cd5\u5728 <a href=\"https:\/\/jamesdefabia.github.io\/docs\/user-guide\/kubectl\/kubectl_get\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">kubectl<\/a> \u547d\u4ee4\u884c\u5de5\u5177\u4e2d\uff0c\u60a8\u53ef\u4ee5\u5c06\u6a21\u677f\u4f20\u9012\u5230 <code>--template<\/code> \u6807\u5fd7\uff0c\u6839\u636e\u9700\u8981\u81ea\u5b9a\u4e49\u8f93\u51fa\u3002<\/p>\n<h2 id=\"Templates-overview\"><span id=\"Templates_overview\" class=\"ez-toc-section\"><\/span><strong>\u6a21\u677f\u603b\u89c8<\/strong><\/h2>\n<p>\u5728 Go \u4e2d\uff0c\u6709\u4e24\u4e2a\u8f6f\u4ef6\u5305\u63d0\u4f9b\u6a21\u677f\u529f\u80fd\uff1a<a href=\"https:\/\/pkg.go.dev\/text\/template\" target=\"_blank\" rel=\"noreferrer noopener\">text\/template<\/a> \u548c <a href=\"https:\/\/pkg.go.dev\/html\/template\" target=\"_blank\" rel=\"noreferrer noopener\">html\/template<\/a> \u8f6f\u4ef6\u5305\u3002 \u4e24\u8005\u5177\u6709\u5b8c\u5168\u76f8\u540c\u7684\u63a5\u53e3\u96c6\uff0c\u552f\u4e00\u7684\u533a\u522b\u662f\u540e\u8005\u81ea\u52a8\u4fdd\u62a4 HTML \u8f93\u51fa\u514d\u53d7\u5404\u79cd\u653b\u51fb\u3002 <code>html\/template<\/code> \u56e0\u6b64\u662f\u751f\u6210 HTML \u8f93\u51fa\u7684\u66f4\u597d\u9009\u62e9\uff0c\u8fd9\u4e5f\u662f\u672c\u6587\u4f7f\u7528 <code>html\/template<\/code> \u8f6f\u4ef6\u5305\u7684\u539f\u56e0\u3002<\/p>\n<p>\u6a21\u677f\u662f\u5b57\u7b26\u4e32\uff0c\u5305\u542b\u4f7f\u7528\u53cc\u82b1\u62ec\u53f7\u62ec\u8d77\u6765\u7684<a href=\"https:\/\/pkg.go.dev\/text\/template#hdr-Actions\" target=\"_blank\" rel=\"noreferrer noopener\">\u64cd\u4f5c<\/a>\u7279\u6b8a\u547d\u4ee4\u3002 \u8fd9\u4e9b\u64cd\u4f5c\u7528\u4e8e\u8bbf\u95ee\u6216\u8bc4\u4f30\u6570\u636e\uff0c\u6216\u63a7\u5236\u6a21\u677f\u7684\u7ed3\u6784\u3002<\/p>\n<p>\u4ee5\u4e0b\u662f\u4e00\u4e2a\u793a\u4f8b\u6a21\u677f\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">tmpl := \"Hello {{.Name}}!\"<\/pre>\n<p>\u00a0<\/p>\n<p>\u4ee5\u4e0a\u6a21\u677f\u53ea\u6709\u4e00\u4e2a\u64cd\u4f5c\uff0c\u8f93\u51fa\u4f20\u9012\u5230\u6a21\u677f\u7684\u6570\u636e\u5bf9\u8c61\u7684 <code>Name<\/code> \u5b57\u6bb5\u7684\u503c\u3002 \u64cd\u4f5c\u4e2d\u7684 <code>.<\/code> \u5b57\u7b26\u6307\u7684\u662f\u4f20\u9012\u5230\u6a21\u677f\u7684\u6570\u636e\u5bf9\u8c61\uff0c<code>.Name<\/code> \u8bbf\u95ee\u5bf9\u8c61\u7684 <code>Name<\/code> \u5b57\u6bb5\u3002<\/p>\n<p>\u8981\u5448\u73b0\u6a21\u677f\uff0c\u5fc5\u987b\u901a\u8fc7 <code>Parse<\/code> \u51fd\u6570\u89e3\u6790\u6a21\u677f\uff0c\u5e76\u4f7f\u7528 <code>Execute<\/code> \u51fd\u6570\u5c06\u5199\u5165\u5668\u548c\u6570\u636e\u5bf9\u8c61\u4f5c\u4e3a\u5b9e\u53c2\u5e76\u5c06\u8f93\u51fa\u5199\u5165\u5199\u5165\u5668\u3002<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">\/\/ Defining the data to pass\ntype User struct {\n    Name string\n}\n\nuser := User{\"James\"}\nt, err := template.New(\"test\").Parse(tmpl)\nif err != nil {\n    panic(err)\n}\nerr = t.Execute(os.Stdout, user)\nif err != nil {\n    panic(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u4e0a\u65b9\u4ee3\u7801\u4f1a\u5c06 <code>Hello James!<\/code> \u8f93\u51fa\u5230\u63a7\u5236\u53f0\u3002<\/p>\n<p>\u9664\u4e86\u8bbf\u95ee\u6570\u636e\u4e4b\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 <code>if<\/code> \u7b49\u64cd\u4f5c\u6709\u6761\u4ef6\u5730\u5448\u73b0\u5185\u5bb9\uff0c\u4f7f\u7528 <code>range<\/code> \u8fed\u4ee3\u96c6\u5408\u3002 \u60a8\u8fd8\u53ef\u4ee5\u5b9a\u4e49\u81ea\u5df1\u7684\u51fd\u6570\u5e76\u5728\u6a21\u677f\u4e2d\u4f7f\u7528\u3002 <a href=\"https:\/\/pkg.go.dev\/text\/template\" target=\"_blank\" rel=\"noreferrer noopener\">\u8fd9\u91cc<\/a>\u662f\u6a21\u677f\u7684\u5b8c\u6574\u6982\u8ff0\u3002<\/p>\n<h2><span id=\"Building_a_Blog_With_Templates\" class=\"ez-toc-section\"><\/span><strong>\u4f7f\u7528\u6a21\u677f\u6784\u5efa\u535a\u5ba2<\/strong><\/h2>\n<p>\u5728\u6309\u7167\u6559\u7a0b\u64cd\u4f5c\u524d\uff0c\u60a8\u9700\u8981\u5728\u7cfb\u7edf\u4e0a\u5b89\u88c5\u5e76\u8bbe\u7f6e Go\u3002 \u60a8\u8fd8\u9700\u8981\u5b89\u88c5 <a href=\"https:\/\/www.jetbrains.com\/go\/\" target=\"_blank\" rel=\"noopener\">GoLand<\/a>\u3002<\/p>\n<p>\u60a8\u53ef\u4ee5\u5728 <a title=\"https:\/\/github.com\/JetBrains\/go-code-samples\/tree\/main\/GoBlog\" href=\"https:\/\/github.com\/JetBrains\/go-code-samples\/tree\/main\/GoBlog\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a> \u4e0a\u627e\u5230\u6b64\u6559\u7a0b\u7684\u4ee3\u7801\u3002 \u60a8\u53ef\u4ee5\u968f\u610f\u514b\u9686\u548c\u7814\u7a76\u4ee3\u7801\uff0c\u4e5f\u53ef\u4ee5\u6309\u7167\u6559\u7a0b\u4ece\u5934\u5f00\u59cb\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<h3><span id=\"Creating_a_new_project\" class=\"ez-toc-section\"><\/span><strong>\u521b\u5efa\u65b0\u9879\u76ee<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u542f\u52a8 GoLand\uff0c\u70b9\u51fb <em>New<\/em> <em>project<\/em>\uff08\u65b0\u5efa\u9879\u76ee\uff09\u6309\u94ae\u3002 \u9009\u62e9 <em>Go<\/em> \u9009\u9879\u5e76\u63d0\u4f9b\u4e00\u4e2a\u540d\u79f0\uff0c\u4f8b\u5982\u201cGoBlog\u201d\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290370\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-43.png\" alt=\"\" width=\"1600\" height=\"405\" \/><\/figure>\n<p>\u70b9\u51fb <em>Create<\/em>\uff08\u521b\u5efa\uff09\u521b\u5efa\u9879\u76ee\u3002 \u5728\u9879\u76ee\u7684\u6839\u76ee\u5f55\u4e0b\u521b\u5efa\u6587\u4ef6 <code>main.go<\/code>\uff0c\u5185\u5bb9\u5982\u4e0b\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"database\/sql\"\n    \"github.com\/go-chi\/chi\/v5\"\n    \"github.com\/go-chi\/chi\/v5\/middleware\"\n    _ \"github.com\/mattn\/go-sqlite3\"\n)<\/pre>\n<p>\u00a0<\/p>\n<p>\u6a21\u5757\u5c06\u4ee5\u7ea2\u8272\u9ad8\u4eae\u663e\u793a\u3002 \u8fd9\u662f\u56e0\u4e3a\u8fd9\u4e9b\u6a21\u5757\u5c1a\u672a\u4e0b\u8f7d\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290488\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-53.png\" alt=\"\" width=\"1600\" height=\"682\" \/><\/figure>\n<p>GoLand \u53ef\u4ee5\u81ea\u52a8\u4e3a\u60a8\u4e0b\u8f7d\u3002 \u5c06\u9f20\u6807\u60ac\u505c\u5728\u9ad8\u4eae\u663e\u793a\u7684\u6761\u76ee\u4e0a\uff0c\u7136\u540e\u5728\u51fa\u73b0\u7684\u5f39\u51fa\u7a97\u53e3\u4e2d\u70b9\u51fb <em>Sync dependencies of GoBlog<\/em>\uff08\u540c\u6b65 GoBlog \u7684\u4f9d\u8d56\u9879\uff09\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290477\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-52.png\" alt=\"\" width=\"1600\" height=\"548\" \/><\/figure>\n<p>\u51e0\u79d2\u949f\u540e\uff0c\u6a21\u5757\u5f00\u59cb\u4e0b\u8f7d\uff0c\u7ea2\u8272\u9ad8\u4eae\u663e\u793a\u6d88\u5931\u3002<\/p>\n<p>\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u542f\u7528 <em>Auto-format on save<\/em>\uff08\u4fdd\u5b58\u65f6\u81ea\u52a8\u683c\u5f0f\u5316\uff09\uff0c\u4e00\u65e6\u6587\u4ef6\u88ab\u4fdd\u5b58\uff08\u4f8b\u5982\uff0c\u7a97\u53e3\u5931\u53bb\u7126\u70b9\u65f6\uff09\uff0cGoLand \u5c06\u683c\u5f0f\u5316\u4ee3\u7801\u5e76\u79fb\u9664\u672a\u4f7f\u7528\u7684\u4f9d\u8d56\u9879\u3002 \u5728 <em>Preference \/ Settings <\/em>|<em> Tools <\/em>|<em> Actions on Save <\/em>|<em> Configure autosave options\u2026 <\/em>|<em> Autosave<\/em>\uff08\u504f\u597d\u8bbe\u7f6e\/\u8bbe\u7f6e | \u5de5\u5177 | \u4fdd\u5b58\u65f6\u7684\u64cd\u4f5c | \u914d\u7f6e\u81ea\u52a8\u4fdd\u5b58\u9009\u9879\u2026 | \u81ea\u52a8\u4fdd\u5b58\uff09\u4e2d\u5207\u6362\u5230\u4e0d\u540c\u7684\u5e94\u7528\u7a0b\u5e8f\u6216\u5185\u7f6e\u7ec8\u7aef\u65f6\uff0c\u53ef\u4ee5\u7981\u7528\u81ea\u52a8\u4fdd\u5b58\u3002<\/p>\n<p>\u60a8\u4e5f\u53ef\u4ee5\u624b\u52a8\u6dfb\u52a0\u5bfc\u5165\u3002 <em>Sync Dependencies<\/em>\uff08\u540c\u6b65\u4f9d\u8d56\u9879\uff09\u64cd\u4f5c\u5c06\u8f6f\u4ef6\u5305\u4e0b\u8f7d\u5230\u7f13\u5b58\u5185\u5b58\uff0c\u624b\u52a8\u6dfb\u52a0\u5bfc\u5165\u65f6\uff0c\u60a8\u4f1a\u83b7\u5f97\u81ea\u52a8\u8865\u5168\u5efa\u8bae\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290466\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-51.png\" alt=\"\" width=\"1600\" height=\"731\" \/><\/figure>\n<p>\u4f5c\u4e3a\u7f16\u5199\u670d\u52a1\u5668\u4ee3\u7801\u7684\u7b2c\u4e00\u6b65\uff0c\u5728 <code>main.go<\/code> \u4e2d\u58f0\u660e\u4ee5\u4e0b\u4e24\u4e2a\u5168\u5c40\u53d8\u91cf\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">var router *chi.Mux\nvar db *sql.DB<\/pre>\n<p>\u00a0<\/p>\n<p>\u867d\u7136\u53ef\u4ee5\u4f7f\u7528\u6807\u51c6\u5e93\u8bbe\u7f6e REST \u670d\u52a1\u5668\uff0c\u4f46\u672c\u6587\u5c06\u4e3a\u6b64\u4f7f\u7528 <a href=\"https:\/\/go-chi.io\/#\/README\" target=\"_blank\" rel=\"noreferrer noopener\">chi<\/a> router \u2013 chi \u662f\u6613\u4e8e\u4f7f\u7528\u3001\u8f7b\u91cf\u7ea7\u4e14\u901f\u5ea6\u6781\u5feb\u7684 router\uff0c\u6346\u7ed1\u4e86\u8bb8\u591a\u529f\u80fd\u3002<\/p>\n<p><code>router<\/code> \u5c06\u5b58\u50a8 chi router \u5b9e\u4f8b\uff0c<code>db<\/code> \u5c06\u5b58\u50a8\u6570\u636e\u5e93\u5bf9\u8c61\u3002<\/p>\n<p>\u5b9a\u4e49 <code>Article<\/code> \u7ed3\u6784\uff0c\u5b83\u5c06\u5177\u6709\u6807\u9898\u3001\u5185\u5bb9\u548c ID\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">type Article struct {\n    ID      int           `json:\"id\"`\n    Title   string        `json:\"title\"`\n    Content template.HTML `json:\"content\"`\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6ce8\u610f\uff0c\u5bf9\u4e8e <code>Content<\/code>\uff0c\u4f7f\u7528\u7684\u662f <code>template.HTML<\/code> \u800c\u4e0d\u662f <code>string<\/code>\u3002 \u56e0\u4e3a\u5185\u5bb9\u5c06\u662f\u5bcc\u6587\u672c\u5e76\u5448\u73b0\u4e3a HTML\uff0c\u9700\u8981\u4f7f\u7528 <code>template.HTML<\/code> \u9632\u6b62 HTML \u5728\u5448\u73b0\u6a21\u677f\u65f6\u88ab\u8f6c\u4e49\u3002<\/p>\n<h3><span id=\"Setting_up_the_database\" class=\"ez-toc-section\"><\/span><strong>\u8bbe\u7f6e\u6570\u636e\u5e93<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u521b\u5efa\u6587\u4ef6 <code>db.go<\/code>\uff0c\u7528\u4e8e\u5b58\u653e\u6570\u636e\u5e93\u76f8\u5173\u529f\u80fd\u3002 \u9996\u5148\uff0c\u5bfc\u5165\u5fc5\u8981\u6a21\u5757\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"database\/sql\"\n)<\/pre>\n<p>\u00a0<\/p>\n<p>\u5728\u672c\u6587\u4e2d\uff0c\u60a8\u5c06\u4f7f\u7528 <code>database\/sql<\/code> \u8f6f\u4ef6\u5305\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u3002 \u4f46\u662f\uff0c\u6a21\u677f\u7cfb\u7edf\u4e0d\u53d7\u6570\u636e\u5e93\u8f6f\u4ef6\u5305\u9009\u62e9\u7684\u5f71\u54cd\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u4e0e\u4efb\u4f55\u5176\u4ed6\u6570\u636e\u5e93\u8f6f\u4ef6\u5305\u4e00\u8d77\u4f7f\u7528\u3002<\/p>\n<p>\u672c\u6559\u7a0b\u5c06\u4f7f\u7528 <a href=\"https:\/\/www.sqlite.org\/index.html\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">SQLite<\/a> \u6570\u636e\u5e93\u5b58\u50a8\u6570\u636e\uff0c\u4f46\u60a8\u53ef\u4ee5\u4f7f\u7528\u4efb\u4f55\u5176\u4ed6 SQL \u6570\u636e\u5e93\uff0c\u4f8b\u5982 <a href=\"https:\/\/www.mysql.com\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">MySQL<\/a> \u6216 <a href=\"https:\/\/www.postgresql.org\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">PostgreSQL<\/a>\u3002 \u6709\u5173 <code>database\/sql<\/code> \u652f\u6301\u7684\u6570\u636e\u5e93\u7684\u5b8c\u6574\u5217\u8868\uff0c\u8bf7\u53c2\u89c1<a href=\"https:\/\/github.com\/golang\/go\/wiki\/SQLDrivers\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">\u6b64\u5904<\/a>\u3002<\/p>\n<p>\u5b9a\u4e49 <code>connect<\/code> \u51fd\u6570\uff0c\u5b83\u5c06\u521b\u5efa\u4e0e\u6570\u636e\u5e93\u7684\u521d\u59cb\u8fde\u63a5\u3002 \u60a8\u5c06\u4f7f\u7528\u672c\u5730\u6587\u4ef6 <code>data.sqlite<\/code> \u5b58\u50a8\u6570\u636e\u3002 \u5982\u679c\u6587\u4ef6\u4e0d\u5b58\u5728\uff0c\u5b83\u5c06\u88ab\u81ea\u52a8\u521b\u5efa\u3002<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func connect() (*sql.DB, error) {\n    var err error\n    db, err = sql.Open(\"sqlite3\", \".\/data.sqlite\")\n    if err != nil {\n        return nil, err\n    }\n\n    sqlStmt := `\n    create table if not exists articles (id integer not null primary key autoincrement, title text, content text);\n    `\n\n    _, err = db.Exec(sqlStmt)\n    if err != nil {\n        return nil, err\n    }\n\n    return db, nil\n}<\/pre>\n<p>\u00a0<\/p>\n<p>SQL \u8bed\u53e5\u5c06\u5728 GoLand \u4e2d\u9ad8\u4eae\u663e\u793a\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290455\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-50.png\" alt=\"\" width=\"1600\" height=\"454\" \/><\/figure>\n<p>GoLand \u5177\u6709\u4e00\u4e2a\u5185\u7f6e<a href=\"https:\/\/www.jetbrains.com\/help\/go\/relational-databases.html\" target=\"_blank\" rel=\"noopener\">\u6570\u636e\u5e93\u63d2\u4ef6<\/a>\uff0c\u53ef\u4ee5\u8fde\u63a5\u5230\u4e0d\u540c\u7684\u6570\u636e\u5e93\uff0c\u7528\u4e8e\u5728 IDE \u5185\u67e5\u8be2\u3001\u521b\u5efa\u548c\u7ba1\u7406\u8868\u3002 \u4f7f\u7528\u63d2\u4ef6\u524d\uff0c\u60a8\u9700\u8981\u914d\u7f6e\u4e00\u4e2a\u6570\u636e\u6e90\u3002 \u70b9\u51fb\u9ad8\u4eae\u663e\u793a\u7684 SQL \u8bed\u53e5\uff0c\u7136\u540e\u5728\u4e0a\u4e0b\u6587\u64cd\u4f5c\u83dc\u5355\uff08\u9ec4\u8272\u706f\u6ce1\u56fe\u6807\uff09\u4e2d\u70b9\u51fb <em>Configure data source<\/em>\uff08\u914d\u7f6e\u6570\u636e\u6e90\uff09\u3002 \u6216\u8005\uff0c\u60a8\u4e5f\u53ef\u4ee5\u70b9\u51fb\u9ad8\u4eae\u663e\u793a\u533a\u57df\u5e76\u6309 <em>Alt+Enter<\/em> (<em>\u2325\u2009&#x21a9;<\/em>) \u67e5\u770b\u53ef\u7528\u7684\u610f\u56fe\u64cd\u4f5c\u3002<\/p>\n<p>\u5728\u51fa\u73b0\u7684\u5bf9\u8bdd\u6846\u4e2d\u70b9\u51fb <code>+<\/code> \u6dfb\u52a0\u65b0\u6570\u636e\u6e90\uff0c\u9009\u62e9 SQLite \u4f5c\u4e3a\u6570\u636e\u5e93\u7c7b\u578b\u3002 \u4e3a\u6570\u636e\u5e93\u63d0\u4f9b\u4e00\u4e2a\u540d\u79f0\uff0c\u4f8b\u5982\u201cDatabase\u201d\uff0c\u5e76\u4f7f\u7528 <code>data.sqlite<\/code> \u4f5c\u4e3a\u6587\u4ef6\u540d\u3002 \u5982\u679c\u6ca1\u6709\u5b89\u88c5\u9002\u5f53\u7684\u6570\u636e\u5e93\u9a71\u52a8\u7a0b\u5e8f\uff0c\u7cfb\u7edf\u4f1a\u63d0\u793a\u5b89\u88c5\u3002 \u51c6\u5907\u5c31\u7eea\u540e\uff0c\u70b9\u51fb <em>OK<\/em>\uff08\u786e\u5b9a\uff09\u4fdd\u5b58\u6570\u636e\u6e90\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290601\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-49-1.png\" alt=\"\" width=\"1500\" height=\"1079\" \/><\/figure>\n<p>\u5982\u679c\u6587\u4ef6\u4e0d\u5b58\u5728\uff0cGoLand \u5c06\u521b\u5efa\u8be5\u6587\u4ef6\u5e76\u8fde\u63a5\u5230\u5b83\uff0c\u7136\u540e\u6253\u5f00\u6570\u636e\u5e93\u63a7\u5236\u53f0\u3002 \u60a8\u53ef\u4ee5\u5728\u8fd9\u91cc\u7f16\u5199 SQL \u67e5\u8be2\u3002 \u60a8\u4e5f\u53ef\u4ee5\u5728\u53f3\u4fa7\u7684 <em>Database<\/em>\uff08\u6570\u636e\u5e93\uff09\u5de5\u5177\u7a97\u53e3\u4e2d\u770b\u5230\u65b0\u521b\u5efa\u7684\u6570\u636e\u5e93\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290612\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-48-1.png\" alt=\"\" width=\"1592\" height=\"849\" \/><\/figure>\n<p>\u8fd4\u56de\u4ee3\u7801\uff0c\u5c06\u5149\u6807\u653e\u5728 <code>create table<\/code> SQL \u8bed\u53e5\u4e0a\uff0c\u7136\u540e\u6309 <em>Ctrl+Enter<\/em> (<em>\u2318&#x21a9;<\/em>)\u3002 \u4ece\u5f39\u51fa\u7a97\u53e3\u83dc\u5355\u9009\u62e9\u63a7\u5236\u53f0\uff0cGoLand \u5c06\u5728\u63a7\u5236\u53f0\u4e2d\u8fd0\u884c\u67e5\u8be2\u3002<\/p>\n<p>\u60a8\u4f1a\u5728\u65e5\u5fd7\u4e2d\u770b\u5230\u8868\u6210\u529f\u521b\u5efa\uff1bDatabase\uff08\u6570\u636e\u5e93\uff09\u4e2d\u4e5f\u5c06\u6709\u6240\u4f53\u73b0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290662\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-46-1.png\" alt=\"\" width=\"1598\" height=\"857\" \/><\/figure>\n<p>\u6211\u4eec\u6765\u7f16\u5199\u5176\u4f59\u7684\u6570\u636e\u5e93\u51fd\u6570\u3002 <code>dbCreateArticle()<\/code> \u51fd\u6570\u5c06\u4ece <code>Article<\/code> \u7ed3\u6784\u5728\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u65b0\u6587\u7ae0\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func dbCreateArticle(article *Article) error {\n    query, err := db.Prepare(\"insert into articles(title,content) values (?,?)\")\n    defer query.Close()\n\n    if err != nil {\n        return err\n    }\n    _, err = query.Exec(article.Title, article.Content)\n\n    if err != nil {\n        return err\n    }\n\n    return nil\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u8fd9\u91cc\u6709\u4e00\u6761\u51c6\u5907\u597d\u7684\u8bed\u53e5\uff0c\u7528\u4e8e\u5c06\u6587\u7ae0\u63d2\u5165\u6570\u636e\u5e93\u3002 <code>?<\/code> \u5360\u4f4d\u7b26\u5c06\u88ab\u66ff\u6362\u4e3a <code>Article<\/code> \u7ed3\u6784\u7684 <code>Title<\/code> \u548c <code>Content<\/code> \u5b57\u6bb5\u7684\u503c\u3002 \u60a8\u4f1a\u6ce8\u610f\u5230\uff0cGoLand \u5c06\u6b63\u786e\u8bc6\u522b\u5d4c\u5165\u5f0f SQL \u8bed\u53e5\u5e76\u5c06\u5176\u9ad8\u4eae\u663e\u793a\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290526\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-54.png\" alt=\"\" width=\"1369\" height=\"656\" \/><\/figure>\n<p>\u548c\u5148\u524d\u4e00\u6837\uff0c\u60a8\u53ef\u4ee5\u5c06\u5149\u6807\u7f6e\u4e8e SQL \u8bed\u53e5\u4e0a\u5e76\u6309 <em>Ctrl+Enter<\/em> (<em>\u2318&#x21a9;<\/em>) \u8fd0\u884c\u67e5\u8be2\u3002 \u8fd9\u6b21\uff0c\u7cfb\u7edf\u4f1a\u63d0\u793a\u60a8\u4e3a\u5360\u4f4d\u7b26\u63d0\u4f9b\u503c\u3002 \u8f93\u5165\u503c\uff0c\u70b9\u51fb <em>Execute<\/em>\uff08\u6267\u884c\uff09\u3002<\/p>\n<p>\u8bf7\u6ce8\u610f\uff0c\u9700\u8981\u4e3a\u503c\u6dfb\u52a0\u5f15\u53f7\uff0c\u56e0\u4e3a\u8fd9\u4e9b\u503c\u5c06\u88ab\u9010\u5b57\u66ff\u6362\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290553\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-44-1-1.png\" alt=\"\" width=\"1500\" height=\"792\" \/><\/figure>\n<p>\u5728 Database\uff08\u6570\u636e\u5e93\uff09\u5de5\u5177\u7a97\u53e3\u4e2d\u53cc\u51fb\u8868\u540d\u5373\u53ef\u67e5\u770b\u6570\u636e\u5e93\u4e2d\u7684\u6240\u6709\u884c\u3002 \u65b0\u521b\u5efa\u7684\u6587\u7ae0\u4e5f\u5e94\u8be5\u5728\u8fd9\u91cc\u663e\u793a\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290400\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-45.png\" alt=\"\" width=\"1600\" height=\"858\" \/><\/figure>\n<p><code>dbGetAllArticles()<\/code> \u51fd\u6570\u4f1a\u5c06\u6570\u636e\u5e93\u4e2d\u7684\u6240\u6709\u6587\u7ae0\u4f5c\u4e3a <code>Article<\/code> \u7ed3\u6784\u7684\u5207\u7247\u8fd4\u56de\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func dbGetAllArticles() ([]*Article, error) {\n    query, err := db.Prepare(\"select id, title, content from articles\")\n    defer query.Close()\n\n    if err != nil {\n        return nil, err\n    }\n    result, err := query.Query()\n\n    if err != nil {\n        return nil, err\n    }\n    articles := make([]*Article, 0)\n    for result.Next() {\n        data := new(Article)\n        err := result.Scan(\n            &amp;data.ID,\n            &amp;data.Title,\n            &amp;data.Content,\n        )\n        if err != nil {\n            return nil, err\n        }\n        articles = append(articles, data)\n    }\n\n    return articles, nil\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>dbGetArticle()<\/code> \u51fd\u6570\u5c06\u6839\u636e ID \u4ece\u6570\u636e\u5e93\u8fd4\u56de\u4e00\u7bc7\u6587\u7ae0\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func dbGetArticle(articleID string) (*Article, error) {\n    query, err := db.Prepare(\"select id, title, content from articles where id = ?\")\n    defer query.Close()\n\n    if err != nil {\n        return nil, err\n    }\n    result := query.QueryRow(articleID)\n    data := new(Article)\n    err = result.Scan(&amp;data.ID, &amp;data.Title, &amp;data.Content)\n\n    if err != nil {\n        return nil, err\n    }\n\n    return data, nil\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6700\u540e\u4e00\u90e8\u5206\u662f <code>dbUpdateArticle()<\/code> \u548c <code>dbDeleteArticle()<\/code> \u51fd\u6570\uff0c\u5b83\u4eec\u5206\u522b\u4ece\u6570\u636e\u5e93\u66f4\u65b0\u548c\u5220\u9664\u6587\u7ae0\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func dbUpdateArticle(id string, article *Article) error {\n    query, err := db.Prepare(\"update articles set (title, content) = (?,?) where id=?\")\n    defer query.Close()\n\n    if err != nil {\n        return err\n    }\n    _, err = query.Exec(article.Title, article.Content, id)\n\n    if err != nil {\n        return err\n    }\n\n    return nil\n}\n\nfunc dbDeleteArticle(id string) error {\n    query, err := db.Prepare(\"delete from articles where id=?\")\n    defer query.Close()\n\n    if err != nil {\n        return err\n    }\n    _, err = query.Exec(id)\n\n    if err != nil {\n        return err\n    }\n\n    return nil\n}<\/pre>\n<p>\u00a0<\/p>\n<h3><span id=\"Creating_the_routes\" class=\"ez-toc-section\"><\/span><strong>\u521b\u5efa\u8def\u7531<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u6570\u636e\u5e93\u51fd\u6570\u5b8c\u6210\u540e\uff0c\u8fd4\u56de <code>main.go<\/code> \u7f16\u5199\u670d\u52a1\u5668\u7684\u5176\u4f59\u90e8\u5206\u3002 \u4ece\u7f16\u5199 <code>catch()<\/code> \u51fd\u6570\u5f00\u59cb\uff0c\u5b83\u4f1a\u5728\u51fa\u73b0\u9519\u8bef\u65f6\u5f15\u53d1\u5b95\u673a\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func catch(err error) {\n    if err != nil {\n        fmt.Println(err)\n        panic(err)\n    }\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>main()<\/code> \u51fd\u6570\u662f\u8bbe\u7f6e\u8def\u7531\u548c\u4e2d\u95f4\u4ef6\u7684\u5730\u65b9\u3002 \u5728 <code>main.go<\/code> \u4e2d\u6dfb\u52a0\u5982\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func main() {\n    router = chi.NewRouter()\n    router.Use(middleware.Recoverer)\n\n    var err error\n    db, err = connect()\n    catch(err)\n\n    router.Use(ChangeMethod)\n    router.Get(\"\/\", GetAllArticles)\n    router.Route(\"\/articles\", func(r chi.Router) {\n        r.Get(\"\/\", NewArticle)\n        r.Post(\"\/\", CreateArticle)\n        r.Route(\"\/{articleID}\", func(r chi.Router) {\n            r.Use(ArticleCtx)\n            r.Get(\"\/\", GetArticle) \/\/ GET \/articles\/1234\n            r.Put(\"\/\", UpdateArticle)    \/\/ PUT \/articles\/1234\n            r.Delete(\"\/\", DeleteArticle) \/\/ DELETE \/articles\/1234\n            r.Get(\"\/edit\", EditArticle) \/\/ GET \/articles\/1234\/edit\n        })\n    })\n\n    err = http.ListenAndServe(\":8005\", router)\n    catch(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6ce8\u610f <a href=\"https:\/\/pkg.go.dev\/github.com\/go-chi\/chi\/middleware#Recoverer\" target=\"_blank\" rel=\"noreferrer noopener\">Recoverer<\/a> \u4e2d\u95f4\u4ef6\u7684\u4f7f\u7528\u3002 \u5f53 <code>catch()<\/code> \u51fd\u6570\u5f15\u53d1\u5b95\u673a\u65f6\uff0c\u6b64\u4e2d\u95f4\u4ef6\u5c06\u6062\u590d\u670d\u52a1\u5668\uff0c\u4f7f\u7528\u5806\u6808\u8ddf\u8e2a\u8bb0\u5f55\u9519\u8bef\uff0c\u5e76\u5411\u5ba2\u6237\u7aef\u53d1\u9001 500 Internal Server Error\uff08500 \u5185\u90e8\u670d\u52a1\u5668\u9519\u8bef\uff09\u54cd\u5e94\u3002<\/p>\n<p>\u4e0a\u65b9\u4ee3\u7801\u4f1a\u8bbe\u7f6e\u4ee5\u4e0b\u8def\u7531\uff1a<\/p>\n<ul>\n<li><code>GET \/<\/code>\uff1a\u663e\u793a\u6570\u636e\u5e93\u4e2d\u7684\u6240\u6709\u6587\u7ae0\u3002<\/li>\n<li><code>GET \/articles<\/code>\uff1a\u663e\u793a\u7528\u4e8e\u521b\u5efa\u65b0\u6587\u7ae0\u7684\u8868\u5355\u3002<\/li>\n<li><code>POST \/articles<\/code>\uff1a\u5728\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u65b0\u6587\u7ae0\u3002<\/li>\n<li><code>GET \/articles\/{articleID}<\/code>\uff1a\u663e\u793a\u4e00\u7bc7\u6587\u7ae0\u3002<\/li>\n<li><code>PUT \/articles\/{articleID}<\/code>\uff1a\u66f4\u65b0\u6570\u636e\u5e93\u4e2d\u7684\u6587\u7ae0\u3002<\/li>\n<li><code>DELETE \/articles\/{articleID}<\/code>\uff1a\u4ece\u6570\u636e\u5e93\u5220\u9664\u6587\u7ae0\u3002<\/li>\n<li><code>GET \/articles\/{articleID}\/edit<\/code>\uff1a\u663e\u793a\u7528\u4e8e\u7f16\u8f91\u6587\u7ae0\u7684\u8868\u5355\u3002<\/li>\n<\/ul>\n<p>\u9664\u4e86\u8def\u7531\u4e4b\u5916\uff0c\u4ee3\u7801\u8fd8\u8bbe\u7f6e\u4e24\u4e2a\u4e2d\u95f4\u4ef6\uff1a<\/p>\n<ul>\n<li><code>ChangeMethod<\/code>\uff1a\u5982\u679c\u8bf7\u6c42\u65b9\u6cd5\u662f <code>POST<\/code> \u5e76\u4e14\u8868\u5355\u5b57\u6bb5 <code>_method<\/code> \u88ab\u8bbe\u7f6e\u4e3a <code>PUT<\/code> \u6216 <code>DELETE<\/code>\uff0c\u5219\u6b64\u4e2d\u95f4\u4ef6\u4f1a\u5c06\u8bf7\u6c42\u65b9\u6cd5\u66f4\u6539\u4e3a <code>PUT<\/code> \u6216 <code>DELETE<\/code>\u3002 \u6b64\u9879\u4e3a\u5fc5\u9700\uff0c\u56e0\u4e3a HTML \u8868\u5355\u4ec5\u652f\u6301 <code>GET<\/code> \u548c <code>POST<\/code> \u65b9\u6cd5\u3002<\/li>\n<li><code>ArticleCtx<\/code>\uff1a\u6b64\u4e2d\u95f4\u4ef6\u5c06\u4ece\u6570\u636e\u5e93\u83b7\u53d6\u6587\u7ae0\u5e76\u5c06\u5176\u5b58\u50a8\u5728\u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e2d\u3002 \u5b83\u5c06\u88ab <code>\/articles\/{articleID}<\/code> \u8def\u5f84\u4e0b\u7684\u8def\u7531\u4f7f\u7528\u3002<\/li>\n<\/ul>\n<p>\u6211\u4eec\u5148\u6765\u7f16\u5199 <code>ChangeMethod<\/code> \u51fd\u6570\u3002 \u5982\u524d\u6240\u8ff0\uff0c\u5b83\u5c06\u67e5\u627e <code>_method<\/code> \u8868\u5355\u5143\u7d20\u5e76\u76f8\u5e94\u5730\u66f4\u6539\u8bf7\u6c42\u65b9\u6cd5\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func ChangeMethod(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        if r.Method == http.MethodPost {\n            switch method := r.PostFormValue(\"_method\"); method {\n            case http.MethodPut:\n                fallthrough\n            case http.MethodPatch:\n                fallthrough\n            case http.MethodDelete:\n                r.Method = method\n            default:\n            }\n        }\n        next.ServeHTTP(w, r)\n    })\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>ArticleCtx<\/code> \u4e2d\u95f4\u4ef6\u5c06\u4ece URL \u5f62\u53c2\u8bbf\u95ee\u6587\u7ae0 ID\uff0c\u5e76\u4ece\u6570\u636e\u5e93\u63d0\u53d6\u6587\u7ae0\u3002 \u5982\u679c\u627e\u5230\u6587\u7ae0\uff0c\u5b83\u5c06\u88ab\u5b58\u50a8\u5728\u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e2d\u3002 \u5982\u679c\u627e\u4e0d\u5230\u6587\u7ae0\uff0c\u4e2d\u95f4\u4ef6\u4f1a\u8fd4\u56de 404 \u72b6\u6001\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func ArticleCtx(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        articleID := chi.URLParam(r, \"articleID\")\n        article, err := dbGetArticle(articleID)\n        if err != nil {\n            fmt.Println(err)\n            http.Error(w, http.StatusText(404), 404)\n            return\n        }\n        ctx := context.WithValue(r.Context(), \"article\", article)\n        next.ServeHTTP(w, r.WithContext(ctx))\n    })\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>GetAllArticles()<\/code> \u51fd\u6570\u5c06\u4f7f\u7528 <code>dbGetAllArticles()<\/code> \u51fd\u6570\u4ece\u6570\u636e\u5e93\u63d0\u53d6\u6240\u6709\u6587\u7ae0\uff0c\u5e76\u901a\u8fc7\u5448\u73b0\u6a21\u677f\u5c06\u5176\u663e\u793a\u5728\u7f51\u9875\u4e0a\u3002 \u73b0\u5728\uff0c\u5c06\u5448\u73b0\u90e8\u5206\u7559\u7a7a\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func GetAllArticles(w http.ResponseWriter, r *http.Request) {\n    articles, err := dbGetAllArticles()\n    catch(err)\n    fmt.Println(articles)\n\n    \/\/TODO: Render template\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>NewArticle()<\/code> \u51fd\u6570\u5c06\u663e\u793a\u7528\u4e8e\u521b\u5efa\u65b0\u6587\u7ae0\u7684\u8868\u5355\u3002 \u5b83\u4e0d\u9700\u8981\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\uff0c\u56e0\u6b64\u975e\u5e38\u57fa\u7840\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func NewArticle(w http.ResponseWriter, r *http.Request) {\n    \/\/TODO: Render template\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u63d0\u4ea4\u7528\u4e8e\u521b\u5efa\u65b0\u6587\u7ae0\u7684\u8868\u5355\u540e\uff0c\u5c06\u8c03\u7528 <code>CreateArticle()<\/code> \u51fd\u6570\u3002 \u5b83\u5c06\u4ece\u8868\u5355\u63d0\u53d6 <code>Title<\/code> \u548c <code>Content<\/code> \u5b57\u6bb5\uff0c\u5e76\u4f7f\u7528 <code>dbCreateArticle()<\/code> \u51fd\u6570\u5728\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u65b0\u6587\u7ae0\u3002 \u7136\u540e\uff0c\u5b83\u4f1a\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 <code>\/<\/code> \u9875\u9762\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func CreateArticle(w http.ResponseWriter, r *http.Request) {\n    title := r.FormValue(\"title\")\n    content := r.FormValue(\"content\")\n    article := &amp;Article{\n        Title:   title,\n        Content: template.HTML(content),\n    }\n\n    err := dbCreateArticle(article)\n    catch(err)\n    http.Redirect(w, r, \"\/\", http.StatusFound)\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>GetArticle()<\/code> \u51fd\u6570\u5c06\u663e\u793a\u4e00\u7bc7\u6587\u7ae0\u3002 \u5f97\u76ca\u4e8e <code>ArticleCtx<\/code> \u4e2d\u95f4\u4ef6\uff0c\u60a8\u4e0d\u9700\u8981\u5728\u8fd9\u91cc\u63d0\u53d6\u6587\u7ae0 \u2013 \u60a8\u53ef\u4ee5\u7b80\u5355\u5730\u4ece\u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e2d\u83b7\u5f97\u5b83\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func GetArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n    fmt.Println(article)\n    \/\/TODO: Render template\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>EditArticle<\/code> \u51fd\u6570\u5c06\u663e\u793a\u7528\u4e8e\u7f16\u8f91\u6587\u7ae0\u7684\u8868\u5355\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func EditArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n    fmt.Println(article)\n    \/\/ TODO: Render template\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u63d0\u4ea4\u7528\u4e8e\u7f16\u8f91\u6587\u7ae0\u7684\u8868\u5355\u540e\uff0c\u5c06\u8c03\u7528 <code>UpdateArticle()<\/code> \u51fd\u6570\u3002 \u5b83\u5c06\u4ece\u8868\u5355\u4e2d\u63d0\u53d6 <code>Title<\/code> \u548c <code>Content<\/code> \u5b57\u6bb5\uff0c\u5e76\u4f7f\u7528 <code>dbUpdateArticle()<\/code> \u51fd\u6570\u5728\u6570\u636e\u5e93\u4e2d\u66f4\u65b0\u6587\u7ae0\u3002 \u7136\u540e\uff0c\u5b83\u4f1a\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 <code>\/articles\/{articleID}<\/code> \u9875\u9762\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func UpdateArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n\n    title := r.FormValue(\"title\")\n    content := r.FormValue(\"content\")\n    newArticle := &amp;Article{\n        Title:   title,\n        Content: template.HTML(content),\n    }\n\n    err := dbUpdateArticle(strconv.Itoa(article.ID), newArticle)\n    catch(err)\n    http.Redirect(w, r, fmt.Sprintf(\"\/articles\/%d\", article.ID), http.StatusFound)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6700\u540e\uff0c<code>DeleteArticle()<\/code> \u51fd\u6570\u5c06\u4ece\u6570\u636e\u5e93\u5220\u9664\u6587\u7ae0\u5e76\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 <code>\/<\/code> \u9875\u9762\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func DeleteArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n    err := dbDeleteArticle(strconv.Itoa(article.ID))\n    catch(err)\n\n    http.Redirect(w, r, \"\/\", http.StatusFound)\n}<\/pre>\n<p>\u00a0<\/p>\n<h3><span id=\"Rendering_templates\" class=\"ez-toc-section\"><\/span><strong>\u5448\u73b0\u6a21\u677f<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u8def\u7531\u73b0\u5df2\u51c6\u5907\u5c31\u7eea\uff0c\u4e0b\u4e00\u6b65\u662f\u5448\u73b0\u6a21\u677f\u3002 \u521b\u5efa <code>templates<\/code> \u76ee\u5f55\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a <code>index.html<\/code> \u6587\u4ef6\uff0c\u6587\u4ef6\u5c06\u5728 <code>GetAllArticles()<\/code> \u51fd\u6570\u4e2d\u5448\u73b0\u3002 \u5c06\u4ee5\u4e0b\u4ee3\u7801\u6dfb\u52a0\u5230\u6587\u4ef6\u4e2d\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;meta charset=\"UTF-8\"&gt;\n    &lt;title&gt;All articles&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n{{if eq (len .) 0}}\nNothing to see here\n{{end}}\n{{range .}}\n&lt;div&gt;\n    &lt;a href=\"\/articles\/{{.ID}}\"&gt;{{.Title}}&lt;\/a&gt;\n&lt;\/div&gt;\n{{end}}\n&lt;p&gt;\n  &lt;a href=\"\/articles\"&gt;Create new article&lt;\/a&gt;\n\n&lt;\/p&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>\u00a0<\/p>\n<p>\u6b64\u6a21\u677f\u4f7f\u7528 <code>if<\/code> \u548c <code>range<\/code> \u6a21\u677f\u51fd\u6570\u3002 <code>if<\/code> \u51fd\u6570\u5c06\u68c0\u67e5\u6587\u7ae0\u6570\u662f\u5426\u7b49\u4e8e\u96f6\u3002 \u5982\u679c\u662f\uff0c\u5b83\u5c06\u663e\u793a <code>Nothing to see here<\/code> \u6d88\u606f\uff0c\u5426\u5219\uff0c\u5b83\u5c06\u663e\u793a\u6587\u7ae0\u5217\u8868\u3002 <code>range<\/code> \u51fd\u6570\u5c06\u904d\u5386\u5e76\u663e\u793a\u6587\u7ae0\u5217\u8868\u3002 <code>.<\/code> \u53d8\u91cf\u6307\u7684\u662f\u4f20\u9012\u5230\u6a21\u677f\u7684\u6240\u6709\u6587\u7ae0\u7684\u5207\u7247\u3002 \u5728 <code>range<\/code> \u51fd\u6570\u4f53\u5185\uff0c<code>.<\/code> \u53d8\u91cf\u6307\u7684\u662f\u4e00\u7bc7\u6587\u7ae0\u3002 \u6587\u7ae0\u7684 <code>ID<\/code> \u548c <code>Title<\/code> \u5b57\u6bb5\u4f7f\u7528\u70b9\u8868\u793a\u6cd5\u8bbf\u95ee\u3002<\/p>\n<p>\u66f4\u65b0 <code>GetAllArticles()<\/code> \u51fd\u6570\u4ee5\u5448\u73b0\u6a21\u677f\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func GetAllArticles(w http.ResponseWriter, r *http.Request) {\n    articles, err := dbGetAllArticles()\n    catch(err)\n\n    t, _ := template.ParseFiles(\"templates\/index.html\")\n    err = t.Execute(w, articles)\n    catch(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6765\u770b\u4e00\u4e0b\u5e94\u7528\u76ee\u524d\u7684\u6837\u5b50\u3002 \u53f3\u952e\u70b9\u51fb <em>Project<\/em>\uff08\u9879\u76ee\uff09\u8fb9\u680f\u4e2d\u7684\u9879\u76ee\u540d\u79f0\uff0c\u7136\u540e\u8f6c\u5230 <em>Run<\/em>\uff08\u8fd0\u884c\uff09\u83dc\u5355\u9879\uff0c\u70b9\u51fb <em>go build GoBlog<\/em>\u3002 \u8fd9\u5c06\u6784\u5efa\u5e76\u8fd0\u884c\u9879\u76ee\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290752\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-55-2.png\" alt=\"\" width=\"1500\" height=\"1166\" \/><\/figure>\n<p>\u6253\u5f00\u6d4f\u89c8\u5668\uff0c\u5bfc\u822a\u5230 <code>http:\/\/localhost:8005<\/code>\u3002 \u8fd9\u5c06\u663e\u793a\u5148\u524d\u521b\u5efa\u7684\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290821\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-56-2.png\" alt=\"\" width=\"1500\" height=\"249\" \/><\/figure>\n<p>\u63a5\u4e0b\u6765\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u7528\u4e8e\u521b\u5efa\u65b0\u6587\u7ae0\u7684\u8868\u5355\u3002 \u7531\u4e8e <code>Content<\/code> \u5b57\u6bb5\u662f\u5bcc\u6587\u672c\uff0c\u9700\u8981\u5bcc\u6587\u672c\u7f16\u8f91\u5668\u3002 \u672c\u6587\u5c06\u4f7f\u7528 <a href=\"https:\/\/www.tiny.cloud\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">TinyMCE<\/a> \u7f16\u8f91\u5668\u3002 \u60a8\u9700\u8981<a href=\"https:\/\/www.tiny.cloud\/auth\/signup\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">\u6ce8\u518c<\/a>\u4e00\u4e2a\u514d\u8d39\u5e10\u6237\u6765\u83b7\u5f97 API \u5bc6\u94a5\u3002 \u83b7\u5f97 API \u5bc6\u94a5\u540e\uff0c\u5728 <code>templates<\/code> \u76ee\u5f55\u4e2d\u521b\u5efa\u4e00\u4e2a <code>new.html<\/code> \u6587\u4ef6\uff0c\u5411\u5176\u4e2d\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;meta charset=\"UTF-8\"&gt;\n    &lt;title&gt;Create a new article&lt;\/title&gt;\n    &lt;script src=\"https:\/\/cdn.tiny.cloud\/1\/no-api-key\/tinymce\/6\/tinymce.min.js\" referrerpolicy=\"origin\"&gt;&lt;\/script&gt;\n    &lt;script&gt;\n        tinymce.init({\n            selector: '#mytextarea',\n        });\n    &lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;h1&gt;Create a new article&lt;\/h1&gt;\n&lt;form method=\"post\" action=\"\/articles\"&gt;\n    &lt;input id=\"title\" type=\"text\" name=\"title\" placeholder=\"Enter the title\"&gt;\n    &lt;textarea id=\"mytextarea\" name=\"content\"&gt;&lt;\/textarea&gt;\n    &lt;button id=\"submit\" type=\"submit\"&gt;Create&lt;\/button&gt;\n&lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>\u00a0<\/p>\n<p>\u60a8\u9700\u8981\u5c06\u4e0a\u8ff0\u4ee3\u7801\u4e2d\u7684 <code>no-api-key<\/code> \u6362\u4e3a TinyMCE API \u5bc6\u94a5\u3002<\/p>\n<p>\u6ce8\u610f\uff0c\u6b64\u6587\u4ef6\u4ec5\u662f\u663e\u793a\u4e00\u4e2a\u8868\u5355\u3002 \u5b83\u4e0d\u662f\u4e25\u683c\u610f\u4e49\u4e0a\u7684\u6a21\u677f\uff0c\u800c\u662f\u4e00\u4e2a\u7b80\u5355\u7684 HTML \u6587\u4ef6\uff0c\u56e0\u6b64\u65e0\u9700\u4f7f\u7528 <code>template.ParseFiles()<\/code> \u51fd\u6570\u8fdb\u884c\u89e3\u6790\u3002 \u4f7f\u7528 <code>http.ServeFile()<\/code> \u51fd\u6570\u5373\u53ef\u3002 \u5c06\u4ee5\u4e0b\u884c\u6dfb\u52a0\u5230 <code>NewArticle<\/code>() \u51fd\u6570\u4e2d\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">http.ServeFile(w, r, \"templates\/new.html\")<\/pre>\n<p>\u00a0<\/p>\n<p>\u60a8\u53ef\u4ee5\u8bbf\u95ee <code>http:\/\/localhost:8005\/articles<\/code> \u67e5\u770b\u8868\u5355\u7684\u5b9e\u9645\u8fd0\u4f5c\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290832\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-57-2.png\" alt=\"\" width=\"1498\" height=\"503\" \/><\/figure>\n<p>\u5982\u679c\u670d\u52a1\u5668\u6b63\u5728\u8fd0\u884c\uff0c\u8bf7\u5c06\u5176\u91cd\u542f\u3002 \u4e3a\u6b64\uff0c\u8bf7\u4f7f\u7528\u914d\u7f6e\u83dc\u5355\u65c1\u8fb9\u7684\u91cd\u542f\u6309\u94ae\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290809\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-58-1.png\" alt=\"\" width=\"1500\" height=\"714\" \/><\/figure>\n<p>\u6bcf\u6b21\u66f4\u6539 Go \u6587\u4ef6\u65f6\uff0c\u60a8\u90fd\u9700\u8981\u91cd\u65b0\u542f\u52a8\u670d\u52a1\u5668\u3002 \u6559\u7a0b\u4e2d\u6ca1\u6709\u660e\u786e\u63d0\u5230\u8fd9\u4e00\u70b9\u3002<\/p>\n<h3><span id=\"Nested_templates\" class=\"ez-toc-section\"><\/span><strong>\u5d4c\u5957\u6a21\u677f<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u60a8\u53ef\u80fd\u5df2\u7ecf\u6ce8\u610f\u5230\uff0c\u5230\u76ee\u524d\u4e3a\u6b62\u521b\u5efa\u7684\u4e24\u4e2a\u6a21\u677f\u5171\u4eab\u4e86\u5927\u91cf\u4ee3\u7801\u3002 HTML \u9875\u9762\u7684\u57fa\u672c\u7ed3\u6784\u5728\u4e24\u4e2a\u6a21\u677f\u4e2d\u662f\u76f8\u540c\u7684 \u2013 \u552f\u4e00\u7684\u533a\u522b\u662f\u9875\u9762\u7684\u6807\u9898\u3001\u5185\u5bb9\u548c\u811a\u672c\u3002 \u4f7f\u7528\u5d4c\u5957\u6a21\u677f\u53ef\u4ee5\u907f\u514d\u5728\u591a\u4e2a\u6a21\u677f\u4e2d\u91cd\u590d\u76f8\u540c\u7684\u4ee3\u7801\u3002 \u5728 <code>templates<\/code> \u76ee\u5f55\u4e2d\u521b\u5efa\u4e00\u4e2a <code>base.html<\/code> \u6587\u4ef6\uff0c\u5411\u5176\u4e2d\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;title&gt;{{ template \"title\" . }}&lt;\/title&gt;\n  {{ template \"scripts\" }}\n&lt;\/head&gt;\n&lt;body&gt;\n{{ template \"body\" . }}\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>\u00a0<\/p>\n<p>\u6b64\u6a21\u677f\u4f7f\u7528\u5176\u4ed6\u4e09\u4e2a\u6a21\u677f\uff1a<code>title<\/code>\u3001<code>scripts<\/code> \u548c <code>body<\/code>\u3002 \u60a8\u5c06\u5b9a\u4e49\u8fd9\u4e9b\u6a21\u677f\u7684\u4e0d\u540c\u7248\u672c\uff0c\u5b83\u4eec\u5c06\u5728 <code>base.html<\/code> \u4e2d\u88ab\u66ff\u6362\u3002 \u8fd9\u4e9b\u6a21\u677f\u5c06\u7528\u4e8e\u663e\u793a\u9875\u9762\u7684\u6807\u9898\u3001\u811a\u672c\u548c\u6b63\u6587\u3002 \u53e6\u5916\uff0c\u8bf7\u6ce8\u610f\uff0c<code>title<\/code> \u548c <code>body<\/code> \u6a21\u677f\u4f7f\u7528 <code>.<\/code> \u53d8\u91cf\u4f20\u9012\u5f53\u524d\u5bf9\u8c61\uff0c\u8fd9\u4f7f\u5d4c\u5957\u6a21\u677f\u80fd\u591f\u5728\u9700\u8981\u65f6\u8bbf\u95ee\u5b83\u3002<\/p>\n<p>\u5c06 <code>index.html<\/code> \u7684\u5185\u5bb9\u66ff\u6362\u4e3a\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">{{define \"title\"}}All articles{{end}}\n{{define \"scripts\"}}{{end}}\n{{define \"body\"}}\n{{if eq (len .) 0}}\nNothing to see here\n{{end}}\n{{range .}}\n&lt;div&gt;\n    &lt;a href=\"\/articles\/{{.ID}}\"&gt;{{.Title}}&lt;\/a&gt;\n&lt;\/div&gt;\n{{end}}\n&lt;p&gt;\n    &lt;a href=\"\/articles\"&gt;Create new article&lt;\/a&gt;\n\n&lt;\/p&gt;\n{{end}}<\/pre>\n<p>\u00a0<\/p>\n<p>\u4f7f\u7528 <code>define<\/code> \u51fd\u6570\uff0c\u60a8\u53ef\u4ee5\u5b9a\u4e49\u5d4c\u5957\u6a21\u677f\u3002 \u5728\u8fd9\u91cc\u5c06\u6807\u9898\u3001\u6b63\u6587\u548c\u811a\u672c\uff08\u4e3a\u7a7a\uff0c\u56e0\u4e3a\u6b64\u9875\u9762\u672a\u52a0\u8f7d\u4efb\u4f55\u811a\u672c\uff09\u63d0\u53d6\u5230\u5b83\u4eec\u81ea\u5df1\u7684\u6a21\u677f\u4e2d\u3002<\/p>\n<p>\u4e3a\u6b64\uff0c\u60a8\u9700\u8981\u4fee\u6539 <code>GetAllArticles()<\/code> \u51fd\u6570\u6765\u52a0\u8f7d <code>base.html<\/code> \u6a21\u677f\u4ee5\u53ca <code>index.html<\/code>\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">t, _ := template.ParseFiles(\"templates\/base.html\", \"templates\/index.html\")<\/pre>\n<p>\u00a0<\/p>\n<p>\u6ce8\u610f\u6a21\u677f\u7684\u987a\u5e8f\u3002 <code>base.html<\/code> \u5fc5\u987b\u5728 <code>index.html<\/code> \u4e4b\u524d\u3002<\/p>\n<p>\u5c06 <code>new.html<\/code> \u66ff\u6362\u4e3a\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">{{define \"title\"}}Create new article{{end}}\n{{define \"scripts\"}}\n&lt;script src=\"https:\/\/cdn.tiny.cloud\/1\/no-api-key\/tinymce\/6\/tinymce.min.js\" referrerpolicy=\"origin\"&gt;&lt;\/script&gt;\n&lt;script&gt;\n    tinymce.init({\n        selector: '#mytextarea',\n    });\n&lt;\/script&gt;\n{{end}}\n{{define \"body\"}}\n&lt;form method=\"post\" action=\"\/articles\"&gt;\n    &lt;input type=\"text\" name=\"title\" placeholder=\"Enter the title\"&gt;\n    &lt;textarea id=\"mytextarea\" name=\"content\"&gt;&lt;\/textarea&gt;\n    &lt;button id=\"submit\" type=\"submit\"&gt;Create&lt;\/button&gt;\n&lt;\/form&gt;\n{{end}}<\/pre>\n<p>\u00a0<\/p>\n<p>\u7531\u4e8e <code>new.html<\/code> \u73b0\u5728\u662f\u6a21\u677f\uff0c<code>http.ServeFile<\/code> \u5c06\u4e0d\u518d\u8d77\u4f5c\u7528\u3002 \u60a8\u9700\u8981\u89e3\u6790\u5e76\u6267\u884c\u6a21\u677f\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func NewArticle(w http.ResponseWriter, r *http.Request) {\n    t, _ := template.ParseFiles(\"templates\/base.html\", \"templates\/new.html\")\n    err := t.Execute(w, nil)\n    catch(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6b64\u65f6\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8868\u5355\u521b\u5efa\u65b0\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290880\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-61-1.png\" alt=\"\" width=\"1600\" height=\"446\" \/><\/figure>\n<p>\u70b9\u51fb <em>Create<\/em>\uff08\u521b\u5efa\uff09\u540e\uff0c\u60a8\u5c06\u88ab\u91cd\u5b9a\u5411\u5230\u6839 URL\uff0c\u65b0\u521b\u5efa\u7684\u6587\u7ae0\u5c06\u5728\u5217\u8868\u4e2d\u51fa\u73b0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290891\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-60-1.png\" alt=\"\" width=\"1600\" height=\"137\" \/><\/figure>\n<p>\u60a8\u4f1a\u6ce8\u610f\u5230\u5bcc\u6587\u672c\u4f5c\u4e3a HTML \u5b58\u50a8\u5728\u6570\u636e\u5e93\u4e2d\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290847\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-59.png\" alt=\"\" width=\"1600\" height=\"580\" \/><\/figure>\n<p>\u521b\u5efa\u6587\u4ef6 <code>article.html<\/code>\uff0c\u5b83\u5c06\u663e\u793a\u4e00\u7bc7\u6587\u7ae0\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">{{define \"title\"}}{{.Title}}{{end}}\n{{define \"scripts\"}}{{end}}\n{{define \"body\"}}\n&lt;h1&gt;{{.Title}} &lt;\/h1&gt;\n&lt;div&gt;\n  {{.Content}}\n&lt;\/div&gt;\n&lt;div&gt;\n  &lt;a href=\"\/articles\/{{.ID}}\/edit\"&gt;Edit&lt;\/a&gt;\n  &lt;form action=\"\/articles\/{{.ID}}\" method=\"post\"&gt;\n    &lt;input type=\"hidden\" name=\"_method\" value=\"DELETE\"&gt;\n    &lt;button type=\"submit\"&gt;Delete&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/div&gt;\n{{end}}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6b64\u9875\u9762\u6709\u4e00\u4e2a\u6307\u5411\u7f16\u8f91\u9875\u9762\u7684\u94fe\u63a5\u548c\u4e00\u4e2a\u5220\u9664\u6587\u7ae0\u7684\u8868\u5355\u3002 \u6ce8\u610f\u9690\u85cf\u7684 <code>_method<\/code> \u5143\u7d20\u3002 \u5982\u524d\u6587\u6240\u8ff0\uff0c\u6b64\u5143\u7d20\u4f1a\u5c06\u8bf7\u6c42\u8f6c\u6362\u4e3a <code>ChangeMethod<\/code> \u4e2d\u95f4\u4ef6\u63d0\u4f9b\u7684 <code>DELETE<\/code> \u8bf7\u6c42\u3002<\/p>\n<p>\u4fee\u6539 <code>GetArticle<\/code> \u51fd\u6570\u4ee5\u5448\u73b0\u6a21\u677f\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func GetArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n    t, _ := template.ParseFiles(\"templates\/base.html\", \"templates\/article.html\")\n    err := t.Execute(w, article)\n    catch(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u70b9\u51fb\u9996\u9875\u4e0a\u6587\u7ae0\u7684\u6807\u9898\u5c06\u663e\u793a\u8be5\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290938\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-64-1.png\" alt=\"\" width=\"1600\" height=\"258\" \/><\/figure>\n<h3><span id=\"Image_uploading\" class=\"ez-toc-section\"><\/span><strong>\u56fe\u50cf\u4e0a\u4f20<\/strong><\/h3>\n<p>[<a title=\"#Templates-overview\" href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>TinyMCE \u9ed8\u8ba4\u652f\u6301\u56fe\u50cf\u4e0a\u4f20\u3002 \u60a8\u53ef\u4ee5\u5c06\u56fe\u50cf\u62d6\u653e\u5230\u7f16\u8f91\u5668\u4e2d\uff0c\u5b83\u4eec\u5c06\u88ab\u8f6c\u6362\u4e3a base64 \u5b57\u7b26\u4e32\u5e76\u5b58\u50a8\u4e3a <code>Content<\/code> \u5b57\u6bb5\u7684\u4e00\u90e8\u5206\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290915\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-63.png\" alt=\"\" width=\"1600\" height=\"197\" \/><\/figure>\n<p>\u56fe\u50cf\u4e5f\u5c06\u663e\u793a\u5728\u6587\u7ae0\u9875\u9762\u4e0a\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-290960\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-62-1.png\" alt=\"\" width=\"1596\" height=\"946\" \/><\/figure>\n<p>\u4f46\u662f\uff0c\u5c06\u56fe\u50cf\u5b58\u50a8\u4e3a base64 \u5b57\u7b26\u4e32<a href=\"https:\/\/stackoverflow.com\/a\/9723812\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">\u5e76\u4e0d\u662f\u597d\u7684\u505a\u6cd5<\/a>\uff0c\u56e0\u6b64\uff0c\u6211\u4eec\u6765\u6dfb\u52a0\u5bf9\u56fe\u50cf\u4e0a\u4f20\u7684\u652f\u6301\u3002<\/p>\n<p>\u5411 router \u6dfb\u52a0\u4e24\u6761\u65b0\u8def\u7531\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func main() {\n    ...\n    router.Use(ChangeMethod)\n    router.Get(\"\/\", GetAllArticles)\n    router.Post(\"\/upload\", UploadHandler) \/\/ Add this\n    router.Get(\"\/images\/*\", ServeImages) \/\/ Add this\n    router.Route(\"\/articles\", func(r chi.Router) {\n        ...\n    })\n\n    ...\n}<\/pre>\n<p>\u00a0<\/p>\n<p><code>\/upload<\/code> \u8def\u7531\u5c06\u5904\u7406\u56fe\u50cf\u4e0a\u4f20\u5e76\u5c06\u5176\u4f5c\u4e3a\u6587\u4ef6\u5b58\u50a8\u5728 images \u76ee\u5f55\u4e2d\uff0c<code>\/images\/*<\/code> \u8def\u7531\u5c06\u63d0\u4f9b\u56fe\u50cf\u3002<\/p>\n<p>\u6211\u4eec\u6765\u7f16\u5199 <code>UploadHandler<\/code> \u51fd\u6570\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func UploadHandler(w http.ResponseWriter, r *http.Request) {\n    const MAX_UPLOAD_SIZE = 10 &lt;&lt; 20 \/\/ Set the max upload size to 10 MB\n    r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE)\n    if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil {\n        http.Error(w, \"The uploaded file is too big. Please choose a file that's less than 10MB in size\", http.StatusBadRequest)\n        return\n    }\n\n    file, fileHeader, err := r.FormFile(\"file\")\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusBadRequest)\n        return\n    }\n\n    defer file.Close()\n\n    \/\/ Create the uploads folder if it doesn't already exist\n    err = os.MkdirAll(\".\/images\", os.ModePerm)\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n    }\n\n    \/\/ Create a new file in the uploads directory\n    filename := fmt.Sprintf(\"\/images\/%d%s\", time.Now().UnixNano(), filepath.Ext(fileHeader.Filename))\n    dst, err := os.Create(\".\" + filename)\n    if err != nil {\n        fmt.Println(err)\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n    }\n\n    defer dst.Close()\n\n    \/\/ Copy the uploaded file to  the specified destination\n    _, err = io.Copy(dst, file)\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n    }\n    fmt.Println(filename)\n    response, _ := json.Marshal(map[string]string{\"location\": filename})\n    w.Header().Set(\"Content-Type\", \"application\/json\")\n    w.WriteHeader(http.StatusCreated)\n    w.Write(response)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u6b64\u51fd\u6570\u4f1a\u5728 <code>images<\/code> \u76ee\u5f55\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u6587\u4ef6\uff0c\u5e76\u5c06\u4f20\u5165\u6587\u4ef6\u590d\u5236\u5230\u8fd9\u4e2a\u65b0\u6587\u4ef6\u4e2d\u3002 \u7136\u540e\uff0c\u5b83\u5c06\u6587\u4ef6\u7684\u4f4d\u7f6e\u4f5c\u4e3a JSON \u54cd\u5e94\u8fd4\u56de\uff0c\u4f9b TinyMCE \u5173\u8054\u56fe\u50cf\u3002<\/p>\n<p><code>ServeImages<\/code> \u51fd\u6570\u975e\u5e38\u76f4\u89c2\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func ServeImages(w http.ResponseWriter, r *http.Request) {\n    fmt.Println(r.URL)\n    fs := http.StripPrefix(\"\/images\/\", http.FileServer(http.Dir(\".\/images\")))\n    fs.ServeHTTP(w, r)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u5b83\u5c06 <code>images<\/code> \u76ee\u5f55\u4e2d\u7684\u6587\u4ef6\u4f5c\u4e3a\u9759\u6001\u6587\u4ef6\u63d0\u4f9b\u3002<\/p>\n<p>\u6700\u540e\u4e00\u6b65\u662f\u8ba9 TinyMCE \u77e5\u9053 <code>\/upload<\/code> \u8def\u7531\u3002 \u5728 <code>new.html<\/code> \u4e2d\uff0c\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u4fee\u6539 <code>tinymce.init<\/code> \u8c03\u7528\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">tinymce.init({\n    selector: '#mytextarea',\n    plugins: 'image',\n    toolbar: 'undo redo | blocks | image | ' +\n        'bold italic backcolor | alignleft aligncenter ' +\n        'alignright alignjustify | bullist numlist outdent indent | ' +\n        'removeformat | help',\n    images_upload_url: \"\/upload\",\n    relative_urls : false,\n    remove_script_host : false,\n    convert_urls : true,\n});<\/pre>\n<p>\u00a0<\/p>\n<p><code>plugins<\/code> \u9009\u9879\u53ef\u4ee5\u52a0\u8f7d\u56fe\u50cf\u4e0a\u4f20\u63d2\u4ef6\uff0c<code>toolbar<\/code> \u9009\u9879\u53ef\u4ee5\u5c06\u56fe\u50cf\u4e0a\u4f20\u6309\u94ae\u6dfb\u52a0\u5230\u5de5\u5177\u680f\u3002 <code>images_upload_url<\/code> \u9009\u9879\u53ef\u4ee5\u6307\u5b9a\u56fe\u50cf\u5c06\u4e0a\u4f20\u5230\u7684\u8def\u7531\u3002 <code>relative_urls<\/code>\u3001<code>remove_script_host<\/code> \u548c <code>convert_urls<\/code> \u9009\u9879\u7528\u4e8e\u5c06 <code>\/upload<\/code> \u8def\u7531\u8fd4\u56de\u7684\u76f8\u5bf9 URL \u8f6c\u6362\u4e3a\u7edd\u5bf9 URL\u3002<\/p>\n<p>\u65b0\u6587\u7ae0\u9875\u9762\u5c06\u5728\u5de5\u5177\u680f\u4e2d\u663e\u793a\u56fe\u50cf\u4e0a\u4f20\u6309\u94ae\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-291037\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-66-1.png\" alt=\"\" width=\"1600\" height=\"448\" \/><\/figure>\n<p>\u70b9\u51fb <em>Upload<\/em>\uff08\u4e0a\u4f20\uff09\u9009\u9879\u5361\uff0c\u4e0a\u4f20\u9700\u8981\u7684\u4efb\u4f55\u56fe\u50cf\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-291048\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-67-1.png\" alt=\"\" width=\"1500\" height=\"610\" \/><\/figure>\n<p>\u5b83\u5c06\u88ab\u4e0a\u4f20\u5e76\u94fe\u63a5\u5230\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-291060\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-68-1.png\" alt=\"\" width=\"1600\" height=\"908\" \/><\/figure>\n<p>\u6700\u540e\uff0c\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u521b\u5efa\u6a21\u677f <code>edit.html<\/code>\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">{{define \"title\"}}Create new article{{end}}\n{{define \"scripts\"}}\n&lt;script src=\"https:\/\/cdn.tiny.cloud\/1\/no-api-key\/tinymce\/6\/tinymce.min.js\" referrerpolicy=\"origin\"&gt;&lt;\/script&gt;\n&lt;script&gt;\n    tinymce.init({\n        selector: '#mytextarea',\n        plugins: 'image',\n        toolbar: 'undo redo | blocks | image | ' +\n            'bold italic backcolor | alignleft aligncenter ' +\n            'alignright alignjustify | bullist numlist outdent indent | ' +\n            'removeformat | help',\n        images_upload_url: \"\/upload\",\n        relative_urls : false,\n        remove_script_host : false,\n        convert_urls : true,\n    });\n&lt;\/script&gt;\n{{end}}\n{{define \"body\"}}\n&lt;form method=\"post\" action=\"\/articles\/{{.ID}}\"&gt;\n  &lt;input type=\"text\" name=\"title\" value=\"{{.Title}}\"&gt;\n  &lt;textarea id=\"mytextarea\" name=\"content\"&gt;{{.Content}}&lt;\/textarea&gt;\n  &lt;input type=\"hidden\" name=\"_method\" value=\"PUT\"&gt;\n  &lt;button id=\"submit\" type=\"submit\" onclick=\"submitForm()\"&gt;Edit&lt;\/button&gt;\n&lt;\/form&gt;\n{{end}}<\/pre>\n<p>\u00a0<\/p>\n<p>\u5b83\u4e0e\u65b0\u6587\u7ae0\u8868\u5355\u975e\u5e38\u76f8\u4f3c\uff0c\u53ea\u662f\u4f7f\u7528\u6587\u7ae0\u7684\u6807\u9898\u548c\u5185\u5bb9\u586b\u5145\u8868\u5355\u3002 \u8868\u5355\u7684 <code>action<\/code> \u7279\u6027\u88ab\u8bbe\u7f6e\u4e3a <code>\/articles\/{id}<\/code>\uff0c <code>_method<\/code> \u5b57\u6bb5\u88ab\u8bbe\u7f6e\u4e3a <code>PUT<\/code>\uff0c\u8868\u660e\u8868\u5355\u7528\u4e8e\u7f16\u8f91\u6587\u7ae0\u3002<\/p>\n<p>\u5728 <code>EditArticle<\/code> \u51fd\u6570\u4e2d\u5448\u73b0\u6b64\u6a21\u677f\uff1a<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\">func EditArticle(w http.ResponseWriter, r *http.Request) {\n    article := r.Context().Value(\"article\").(*Article)\n\n    t, _ := template.ParseFiles(\"templates\/base.html\", \"templates\/edit.html\")\n    err := t.Execute(w, article)\n    catch(err)\n}<\/pre>\n<p>\u00a0<\/p>\n<p>\u73b0\u5728\uff0c\u70b9\u51fb\u6587\u7ae0\u9875\u9762\u4e0a\u7684 <em>Edit<\/em>\uff08\u7f16\u8f91\uff09\u6309\u94ae\u5373\u53ef\u7f16\u8f91\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-291072\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-69-1.png\" alt=\"\" width=\"1465\" height=\"480\" \/><\/figure>\n<p>\u7f16\u8f91\u540e\uff0c\u60a8\u5c06\u88ab\u91cd\u5b9a\u5411\u5230\u540c\u4e00\u9875\u9762\uff0c\u663e\u793a\u66f4\u65b0\u540e\u7684\u6587\u7ae0\u3002<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-291083\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2022\/10\/image-70-1.png\" alt=\"\" width=\"1001\" height=\"502\" \/><\/figure>\n<p>\u60a8\u4e5f\u53ef\u4ee5\u70b9\u51fb <em>Delete<\/em>\uff08\u5220\u9664\uff09\u6309\u94ae\u5220\u9664\u6587\u7ae0\u3002<\/p>\n<h2><span id=\"Conclusion\" class=\"ez-toc-section\"><\/span><strong>\u603b\u7ed3<\/strong><\/h2>\n<p>[<a href=\"https:\/\/blog.jetbrains.com\/go\/2022\/11\/08\/build-a-blog-with-go-templates\/#Templates_overview\">\u8fd4\u56de\u9875\u9996<\/a>]<\/p>\n<p>\u5982\u679c\u60a8\u60f3\u5728\u4e00\u4e2a\u5730\u65b9\u67e5\u770b\u6b64\u9879\u76ee\u7684\u6240\u6709\u4ee3\u7801\uff0c\u53ef\u4ee5\u524d\u5f80<a href=\"https:\/\/github.com\/JetBrains\/go-code-samples\/tree\/main\/GoBlog\" target=\"_blank\" rel=\"noreferrer noopener\">\u6b64\u5904<\/a>\u3002 <a href=\"https:\/\/pkg.go.dev\/text\/template\" target=\"_blank\" rel=\"noreferrer noopener\">Go<\/a> \u4e2d\u7684\u6a21\u677f\u63d0\u4f9b\u4e86\u4e0e\u81ea\u5b9a\u4e49\u8f93\u51fa\u683c\u5f0f\u76f8\u5173\u7684\u5f3a\u5927\u529f\u80fd\u3002 \u7531\u4e8e\u80fd\u591f\u8f93\u51fa\u5b89\u5168\u7684 HTML\uff0c<code>html\/template<\/code> \u8f6f\u4ef6\u5305\u5728 Web \u5f00\u53d1\u4e2d\u5f97\u5230\u5e7f\u6cdb\u5e94\u7528\u3002 \u672c\u6587\u4ecb\u7ecd\u4e86\u5982\u4f55\u4f7f\u7528 <code>html\/template<\/code> \u8f6f\u4ef6\u5305\u521b\u5efa\u4e00\u4e2a\u7b80\u5355\u7684\u535a\u5ba2\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>\u00a0<\/p>\n<p>\u672c\u535a\u6587\u82f1\u6587\u539f\u4f5c\u8005\uff1a<\/p>\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/secure.gravatar.com\/avatar\/?s=200&#038;r=g\" width=\"200\" height=\"200\" alt=\"\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":1297,"featured_media":290499,"comment_status":"closed","ping_status":"closed","template":"","categories":[2347],"tags":[4197,3926,1166],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/310495"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/go"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/1297"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=310495"}],"version-history":[{"count":4,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/310495\/revisions"}],"predecessor-version":[{"id":310508,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/go\/310495\/revisions\/310508"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media\/290499"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=310495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=310495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=310495"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=310495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}