Created
May 25, 2017 22:35
-
-
Save vzsg/3e693e459b7de6fc73493a73ecbed297 to your computer and use it in GitHub Desktop.
HTML5 Routing Compatibility Middleware for Vapor 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Config { | |
public func setup() throws { | |
// ... | |
addConfigurable(middleware: Html5RoutingMiddleware.init, name: "html5") | |
} | |
// ... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// ... | |
"middleware": [ | |
"error", | |
"html5", // <-- make sure this is before "file" | |
"date", | |
"file" | |
], | |
// ... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Vapor | |
import HTTP | |
import Foundation | |
public final class Html5RoutingMiddleware: Middleware { | |
private let loader = DataFile() | |
public let defaultPath: String | |
public init(defaultPath: String) { | |
self.defaultPath = defaultPath | |
} | |
public func respond(to request: Request, chainingTo next: Responder) throws -> Response { | |
do { | |
return try next.respond(to: request) | |
} | |
catch let error as AbortError where error.status == .notFound { | |
guard request.headers["X-Requested-With"] != "XMLHttpRequest" else { | |
throw error | |
} | |
guard | |
let attributes = try? Foundation.FileManager.default.attributesOfItem(atPath: defaultPath), | |
let modifiedAt = attributes[.modificationDate] as? Date, | |
let fileSize = attributes[.size] as? NSNumber | |
else { | |
throw Abort.notFound | |
} | |
var headers: [HeaderKey: String] = [:] | |
// Generate ETag value, "HEX value of last modified date" + "-" + "file size" | |
let fileETag = "\(modifiedAt.timeIntervalSince1970)-\(fileSize.intValue)" | |
headers["ETag"] = fileETag | |
// Check if file has been cached already and return NotModified response if the etags match | |
if fileETag == request.headers["If-None-Match"] { | |
return Response(status: .notModified, headers: headers, body: .data([])) | |
} | |
// Set Content-Type header based on the media type | |
// Only set Content-Type if file not modified and returned above. | |
if | |
let fileExtension = defaultPath.components(separatedBy: ".").last, | |
let type = Request.mediaTypes[fileExtension] | |
{ | |
headers["Content-Type"] = type | |
} | |
// File exists and was not cached, returning content of file. | |
if let fileBody = try? loader.read(at: defaultPath) { | |
return Response(status: .ok, headers: headers, body: .data(fileBody)) | |
} else { | |
print("unable to load path") | |
throw Abort.notFound | |
} | |
} | |
} | |
} | |
extension Html5RoutingMiddleware: ConfigInitializable { | |
public convenience init(config: Config) throws { | |
let path = config.publicDir + (config["html5", "index"]?.string ?? "index.html") | |
self.init(defaultPath: path) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final class Routes: RouteCollection { | |
func build(_ builder: RouteBuilder) throws { | |
// ... | |
// Make sure to remove any "*" and "/" route handlers! | |
// E.g. the api-template has a "*" route that prints the request details. Kill it. | |
// ... | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment