github.com/blend/go-sdk@v1.20220411.3/web/view_result.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package web
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"html/template"
    14  	"net"
    15  	"net/http"
    16  
    17  	"github.com/blend/go-sdk/env"
    18  	"github.com/blend/go-sdk/ex"
    19  	"github.com/blend/go-sdk/logger"
    20  	"github.com/blend/go-sdk/webutil"
    21  )
    22  
    23  // ViewResult is a result that renders a view.
    24  type ViewResult struct {
    25  	ViewName   string
    26  	StatusCode int
    27  	ViewModel  interface{}
    28  	Views      *ViewCache
    29  	Template   *template.Template
    30  }
    31  
    32  // Render renders the result to the given response writer.
    33  func (vr *ViewResult) Render(ctx *Ctx) (err error) {
    34  	// you must set the template to be rendered.
    35  	if vr.Template == nil {
    36  		err = ex.New(ErrUnsetViewTemplate)
    37  		return
    38  	}
    39  
    40  	if ctx.Tracer != nil {
    41  		if typed, ok := ctx.Tracer.(ViewTracer); ok {
    42  			tf := typed.StartView(ctx, vr)
    43  			defer func() {
    44  				tf.FinishView(ctx, vr, err)
    45  			}()
    46  		}
    47  	}
    48  
    49  	ctx.Response.Header().Set(webutil.HeaderContentType, webutil.ContentTypeHTML)
    50  
    51  	// use a pooled buffer if possible
    52  	var buffer *bytes.Buffer
    53  	if vr.Views != nil && vr.Views.BufferPool != nil {
    54  		buffer = vr.Views.BufferPool.Get()
    55  		defer vr.Views.BufferPool.Put(buffer)
    56  	} else {
    57  		buffer = new(bytes.Buffer)
    58  	}
    59  
    60  	err = vr.Template.Execute(buffer, &ViewModel{
    61  		Env: env.Env(),
    62  		Ctx: ctx,
    63  		Status: ViewStatus{
    64  			Text: http.StatusText(vr.StatusCode),
    65  			Code: vr.StatusCode,
    66  		},
    67  		ViewModel: vr.ViewModel,
    68  	})
    69  	if err != nil {
    70  		err = ex.New(err)
    71  		if ctx.App != nil {
    72  			logger.MaybeErrorContext(ctx.Context(), ctx.App.Log, err)
    73  		}
    74  		ctx.Response.WriteHeader(http.StatusInternalServerError)
    75  		_, _ = ctx.Response.Write([]byte(fmt.Sprintf("%+v\n", err)))
    76  		return
    77  	}
    78  
    79  	ctx.Response.WriteHeader(vr.StatusCode)
    80  	_, err = ctx.Response.Write(buffer.Bytes())
    81  	if err != nil {
    82  		if typed, ok := err.(*net.OpError); ok {
    83  			err = ex.New(webutil.ErrNetWrite, ex.OptInner(typed))
    84  			return
    85  		}
    86  		err = ex.New(err)
    87  		return
    88  	}
    89  	return
    90  }