@@ -46,7 +46,23 @@ public extension ServerResponse {
4646 return
4747 }
4848
49- app. render ( template: template, options: options, to: self )
49+ app. render ( template, options) { errorMaybe, contentMaybe in
50+ if let error = errorMaybe {
51+ self . emit ( error: error)
52+ return self . finishRender500IfNecessary ( )
53+ }
54+
55+ guard let content = contentMaybe else {
56+ return self . sendStatus ( 204 )
57+ }
58+
59+ // Wow, this is harder than it looks. we may want to consider a MIMEType
60+ // object as a value :-)
61+ // FIXME: also consider extension of template (.html, .vcf etc)
62+ self . setHeader ( " Content-Type " , detectTypeForContent ( string: content) )
63+
64+ self . send ( content)
65+ }
5066 }
5167
5268 fileprivate func finishRender500IfNecessary( ) {
@@ -57,85 +73,28 @@ public extension ServerResponse {
5773}
5874
5975public extension Express {
60-
61- /**
62- * Locate the rendering engine for a given path and render it with the options
63- * that are passed in.
64- *
65- * Refer to the ``ServerResponse/render`` method for details.
66- *
67- * - Parameters:
68- * - path: the filesystem path to a template.
69- * - options: Any options passed to the rendering engine.
70- * - res: The response the rendering will be sent to.
71- */
72- func render( path: String , options: Any ? , to res: ServerResponse ) {
73- let log = self . log
74- let ext = fs. path. extname ( path)
75- let viewEngine = ext. isEmpty ? defaultEngine : ext
76-
77- guard let engine = engines [ viewEngine] else {
78- log. error ( " Did not find view engine for extension: \( viewEngine) " )
79- res. emit ( error: ExpressRenderingError . unsupportedViewEngine ( viewEngine) )
80- res. finishRender500IfNecessary ( )
81- return
82- }
83-
84- engine ( path, options) { ( results: Any? ... ) in
85- let rc = results. count
86- let v0 = rc > 0 ? results [ 0 ] : nil
87- let v1 = rc > 1 ? results [ 1 ] : nil
88-
89- if let error = v0 {
90- res. emit ( error: ExpressRenderingError
91- . templateError ( error as? Swift . Error ) )
92- log. error ( " template error: " , error)
93- res. writeHead ( 500 )
94- res. end ( )
95- return
96- }
97-
98- guard let result = v1 else { // Hm?
99- log. warn ( " template returned no content: \( path) \( results) " )
100- res. writeHead ( 204 )
101- res. end ( )
102- return
103- }
10476
105- // TBD: maybe support a stream as a result? (result.pipe(res))
106- // Or generators, there are many more options.
107- if !( result is String ) {
108- log. warn ( " template rendering result is not a String: " , result)
109- }
110-
111- let s = ( result as? String ) ?? " \( result) "
112-
113- // Wow, this is harder than it looks when we want to consider a MIMEType
114- // object as a value :-)
115- var setContentType = true
116- if let oldType = res. getHeader ( " Content-Type " ) {
117- let s = ( oldType as? String ) ?? String ( describing: oldType) // FIXME
118- setContentType = ( s == " httpd/unix-directory " ) // a hack for Apache
119- }
120-
121- if setContentType {
122- // FIXME: also consider extension of template (.html, .vcf etc)
123- res. setHeader ( " Content-Type " , detectTypeForContent ( string: s) )
124- }
125-
126- res. writeHead ( 200 )
127- res. write ( s)
128- res. end ( )
129- }
130- }
131-
13277 /**
13378 * Lookup a template with the given name, locate the rendering engine for it,
13479 * and render it with the options that are passed in.
13580 *
136- * Refer to the ``ServerResponse/render`` method for details.
81+ * Example:
82+ * ```swift
83+ * app.render("index", [ "title": "Hello World!" ]) { error, html in
84+ * ...
85+ * }
86+ * ```
87+ *
88+ * Assuming your 'views' directory contains an `index.mustache` file, this
89+ * would trigger the Mustache engine to render the template with the given
90+ * dictionary as input.
91+ *
92+ * When no options are passed in, render will fallback to the `view options`
93+ * setting in the application (TODO: merge the two contexts).
13794 */
138- func render( template: String , options: Any ? , to res: ServerResponse ) {
95+ func render( _ template: String , _ options : Any ? = nil ,
96+ yield: @escaping ( Error ? , String ? ) -> Void )
97+ {
13998 let log = self . log
14099 let cacheOn = settings. enabled ( " view cache " )
141100 let emptyOpts : [ String : Any ] = [ : ]
@@ -147,17 +106,15 @@ public extension Express {
147106 let path = view. path
148107 {
149108 log. trace ( " Using cached view: " , template)
150- return self . render ( path: path, options: viewOptions, to : res )
109+ return self . render ( path: path, options: viewOptions, yield : yield )
151110 }
152111
153112 let viewType : View . Type = ( get ( " view " ) as? View . Type) ?? View . self
154113 let view = viewType. init ( name: template, options: self )
155114 let name = path. basename ( template, path. extname ( template) )
156115 view. lookup ( name) { pathOrNot in
157116 guard let path = pathOrNot else {
158- res. emit ( error: ExpressRenderingError . didNotFindTemplate ( template) )
159- res. finishRender500IfNecessary ( )
160- return
117+ return yield ( ExpressRenderingError . didNotFindTemplate ( template) , nil )
161118 }
162119
163120 view. path = path // cache path
@@ -168,9 +125,57 @@ public extension Express {
168125 }
169126 }
170127
171- self . render ( path: path, options: viewOptions, to : res )
128+ self . render ( path: path, options: viewOptions, yield : yield )
172129 }
173130 }
131+
132+ /**
133+ * Locate the rendering engine for a given path and render it with the options
134+ * that are passed in.
135+ *
136+ * Refer to the ``ServerResponse/render`` method for details.
137+ *
138+ * - Parameters:
139+ * - path: the filesystem path to a template.
140+ * - options: Any options passed to the rendering engine.
141+ * - yield: The result of template being rendered.
142+ */
143+ func render( path: String , options: Any ? ,
144+ yield: @escaping ( Error ? , String ? ) -> Void )
145+ {
146+ let log = self . log
147+ let ext = fs. path. extname ( path)
148+ let viewEngine = ext. isEmpty ? defaultEngine : ext
149+
150+ guard let engine = engines [ viewEngine] else {
151+ log. error ( " Did not find view engine for extension: \( viewEngine) " )
152+ return yield ( ExpressRenderingError . unsupportedViewEngine ( viewEngine) , nil )
153+ }
154+
155+ engine ( path, options) { ( results: Any? ... ) in
156+ if let value = results. first, let error = value {
157+ log. error ( " view engine error: " , error)
158+ yield( ExpressRenderingError . templateError ( error as? Swift . Error ) , nil )
159+ return
160+ }
161+
162+ guard let input = results. dropFirst ( ) . first, let result = input else {
163+ log. warn ( " View engine returned no content for: " , path, results)
164+ return yield ( nil , nil )
165+ }
166+
167+ // TBD: maybe support a stream as a result? (result.pipe(res))
168+ // Or generators, there are many more options.
169+ if !( result is String ) {
170+ log. warn ( " template rendering result is not a String: " , result)
171+ assertionFailure ( " Non-template rendering result \( type ( of: result) ) " )
172+ }
173+
174+ let s = ( result as? String ) ?? " \( result) "
175+ yield( nil , s)
176+ }
177+ }
178+
174179}
175180
176181
0 commit comments