Vapor 3中从数据库加载数据并渲染至视图的正确方式咨询
Correct Implementation for Loading DB Data & Rendering Views in Vapor 3
Hey there! Let's get your Vapor 3 project properly loading database data and rendering it in a view. Your current code has the right idea, but the callback-based approach doesn't fit with Vapor's async Future-driven workflow. Let's break down the fixes and proper implementation:
First, Let's Spot the Issues in Your Current Code
- You’re using a callback (
getContentwith a closure) which doesn’t align with Vapor 3’s Future-based async model—this can lead to unhandled async operations or race conditions. BlogModel.query(on: req).first()returns aFuture<BlogModel?>(an optional wrapped in a Future), but yourmap(to: BlogModel.self)tries to force it to a non-optionalBlogModel—this will cause compile errors or runtime crashes if no blog entry exists.- Your
newpostmethod isn’t properly chaining Futures to return the requiredFuture<View>that Vapor expects from route handlers.
Proper Implementation 1: Assume a Blog Entry Exists (Throw 404 If Not)
This is ideal if you expect a BlogModel entry to always be present. We’ll chain the database query directly to the view render:
func newpost(_ request: Request) throws -> Future<View> { // Step 1: Fetch the first BlogModel from the database return BlogModel.query(on: request).first() // Step 2: Handle the case where no blog entry is found (throw 404) .unwrap(or: Abort(.notFound, reason: "No blog content found")) // Step 3: Use flatMap to render the view (since render returns a Future) .flatMap { blog in // Prepare the context to pass to your Leaf template // Match the key to what you're using in newpost.leaf (e.g., #(content)) let context = ["content": blog.content] // Render the view with the loaded content return try request.view().render("newpost.leaf", context) } }
Proper Implementation 2: Handle Missing Blog Entries Gracefully
If it’s acceptable for no BlogModel to exist, we can provide a default value instead of throwing an error:
func newpost(_ request: Request) throws -> Future<View> { return BlogModel.query(on: request).first() // Step 1: Convert the optional blog to a string (use default if nil) .map { blog in blog?.content ?? "Default placeholder content for your new post" } // Step 2: Render the view with the content (either from DB or default) .flatMap { content in let context = ["content": content] return try request.view().render("newpost.leaf", context) } }
Key Things to Remember
- Future Chaining: Use
flatMapwhen you need to return another Future (likerenderwhich givesFuture<View>). Usemapfor synchronous value transformations (like turning an optional into a string). - Error Handling:
unwrap(or:)is a clean way to enforce that a database entry exists—if it doesn’t, Vapor will automatically send an HTTP 404 response to the client. - View Context: Make sure the keys in your context dictionary match the variables in your
newpost.leaftemplate. For example, if your Leaf file uses#(content)to display the HTML, your context key should be"content".
内容的提问来源于stack exchange,提问作者Nhat
相关产品推荐
相关产品推荐

