From ASP.NET Webforms to ASP.NET MVC to GO
For the past 10 years, I’ve used many different blog platforms like (hosted) Blogger, (webforms-based) DotNetNuke, BlogEngine.NET, and (MVC-based) MiniBlog.
More recently, while learning Google’s GO, I’ve decided to migrate my blog to Journey platform, which allows me to use Ghost Themes, is based on Markdown markup language, and uses SQLite as database.
Building and running GO/Journey on Windows was as easy as this:
- Download and install GO if you don’t have it.
Remember to create a base folder for your GO repositories, and set up environment variables GOPATH pointing to it. Remember to add the installation folder (bin subfolder) to the PATH, and set up environment variable GOROOT in case you modified the default installation folder
- I had to install a MingW (GNU for Windows) because building SQLite (next step) requires gcc. Install x86_64 if you are running on 64-bits.
After installing, add both GO and MinGW to your path:
C:\GO\bin
, C:\Program Files\mingw-w64\x86_64-5.3.0-posix-seh-rt_v4-rev0\mingw64\bin
or C:\MinGW\bin
- Download source code and dependencies:
go get -u github.com/kabukky/journey
- Navigate to Journey folder:
cd /d %GOPATH%\src\github.com\kabukky\journey
- Build it:
go build
- Run it:
journey.exe
I’ve tried some Ghost templates, and costumized them easily, using different things from each template.
Linux and case-sensitivity
One thing that I don’t like in Linux is that everything is case-sensitive, so most server applications treat URLs as case-sensitive and most databases (like PostgreSQL) treat tables and columns as case-sensitive. The “best-practice” for URLs suggest that you should always use lowercase characters, so that you won’t have problems with someone using the wrong case. The “best-practice” for PostgreSQL suggest that you should never use double-quote on object identifiers, so that they are automatically converted to lowercase, so that you won’t have problems by using the wrong case.
In my opinion this “best-practice” is an ugly workaround for what seems to me like a design problem. I really can’t imagine a case where someone would need two URLs with same address but different cases, or two different tables whose names differ only by some uppercases. I also believe that URLs (and even table names) are much more readable when they contain mixed case, because our brain has been trained for that for decades.
PS: I understand why filesystems are designed being case-sensitive, but I strongly disagree that URLs that should be human-readable (and human-memorable) should follow this. And I’m glad that domain names have been designed correctly in this sense.
Having said that, I made a small modification to Journey so that the search of a post by the slug became case insensitive. If the case doesn’t match exactly, it does a 301 redirect to the canonical url, so that Google will always use the correct mixed-case url. (Someone told me that Ghost also has this feature).
Contact Form
I’ve developed a very simple contact form, creating a page at content\pages\Contact\index.html. Then I modified /server/pages.go to handle not only GETs but also POST:
func InitializePages(router *httptreemux.TreeMux) {
// For serving standalone projects or pages saved in in content/pages
router.GET("/pages/*filepath", pagesHandler)
router.POST("/pages/*filepath", postPagesHandler)
}
func postPagesHandler(w http.ResponseWriter, r *http.Request, params map[string]string) {
path := filepath.Join(filenames.PagesFilepath, params["filepath"])
email := r.FormValue("Email")
message := r.FormValue("Message")
name := r.FormValue("Name")
subject := r.FormValue("Subject")
go sendMail(email, message, name, subject)
http.ServeFile(w, r, path+"/"+"index.post.html")
return
}
type SmtpTemplateData struct {
FromEmail string
FromName string
Subject string
Body string
}
type SMTPSettings struct {
Username string
Password string
EmailServer string
Port int
}
func sendMail(fromEmail string, message string, fromName string, subject string) {
const emailTemplate = `From: {{.FromName}}
Subject: {{.Subject}}
{{.Body}}
{{.FromName}} ({{.FromEmail}})
`
var err error
//mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n";
mime := "MIME-version: 1.0;\nContent-Type: text/plain; charset=\"UTF-8\";\n"
//subjectHeader := "Subject: " + encodeRFC2047(subject) + "\n"
subjectHeader := "Subject: " + subject + "\n"
to := "To: " + "[email protected]" + "\n" // this is just what will be displayed in headers
message = message + "\n\n" + fromName + " <" + fromEmail + ">" //append real sender to body, since GMAIL automatically fills from
//msg := []byte(subject + mime + "<html><body></body></html>")
msg := []byte(subjectHeader + to + mime + "\n" + message) // two line breaks before message body
emailUser := &SMTPSettings{"[email protected]", "password", "smtp.gmail.com", 587}
auth := smtp.PlainAuth("",
emailUser.Username,
emailUser.Password,
emailUser.EmailServer)
err = smtp.SendMail(emailUser.EmailServer+":"+strconv.Itoa(emailUser.Port), // in our case, "smtp.google.com:587"
auth,
emailUser.Username,
[]string{"[email protected]"},
msg)
if err != nil {
log.Print("ERROR: attempting to send a mail ", err)
}
return
}
The sendMail() method was based on this sample to send email through Gmail.
Hosting GO on Windows 2012 & IIS 8
I wanted to host the blog on a Windows Server where I already run other websites. Since the server already had other websites running on port 80, I couldn’t simply run journey.exe and use port 80, so I had to use IIS which is already using port 80 and redirecting to different websites according to name-based virtual hosting, which in IIS is called host-header.
The following steps describe how I created a website on IIS for doing the host-header and redirecting requisitions to Journey, hosted on its own port on another IIS process.
First thing is to create a new website. I created a website on IIS using I binded my site to port 80, but using a specific host address (drizin.com.br).
Then, I created a Web.config to redirect all requests that this website catches (at drizin.com.br:80) and redirect them with a reverse proxy to the other port where GO/Journey will listen (8084).
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Reverse Proxy to GO" stopProcessing="false">
<match url="^(.*)" />
<action type="Rewrite" url="http://localhost:8084/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
For this reverse-proxy to work you need to install Application Request Routing using Web Platform Installer.
After installing, restart IIS Manager, and enable it here:
Now we need to prepare Journey to run on port 8084. Of course you can just run it in console or as a scheduled task, but you would miss all management features of IIS.
For hosting a standalone server (journey.exe) inside IIS you need to install HTTP Platform Handler using Web Platform Installer.
Then I created a new website for listening on port 8084 to host the GO/Journey process, and created a Web.config to host this external process, and redirect all connections from port 8084 to Journey process using HTTP Platform Handler.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<!-- need to install HTTP PLATFORM HANDLER 1.2 -->
<!-- need to give write permissions to IIS_IUSRS, since AppPool runs under ApplicationPoolIdentity -->
<handlers>
<add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
</handlers>
<httpPlatform processPath="E:\RD\Drizin\journey.exe"
arguments="-log=E:\RD\Drizin\log.txt -http-port=%HTTP_PLATFORM_PORT%"
startupTimeLimit="60">
</httpPlatform>
</system.webServer>
</configuration>
Please note that this second website is bound to port 8084, but the journey.exe is bound to another random port (HTTP_PLATFORM_PORT), and HTTP Platform Handler forwards all 8084 connections to the correct journey port.
I also had to give write permissions to IIS_USRS on the database folder (journey\content\data).
And that’s it. Blog is up and running. :-)