// WriteResponseObject writes the status code and response object to the HttpResponseWriter in // the specified context, in the format best suited based on the request. // // Goweb uses the WebCodecService to decide which codec to use when responding // see http://godoc.org/github.com/stretchr/codecs/services#WebCodecService for more information. // // This method should be used when the Goweb Standard Response Object does not satisfy the needs of // the API, but other Respond* methods are recommended. func (a *GowebAPIResponder) WriteResponseObject(ctx context.Context, status int, responseObject interface{}) error { service := a.GetCodecService() acceptHeader := ctx.HttpRequest().Header.Get("Accept") extension := ctx.FileExtension() hasCallback := len(ctx.QueryValue(CallbackParameter)) > 0 codec, codecError := service.GetCodecForResponding(acceptHeader, extension, hasCallback) if codecError != nil { return codecError } options := ctx.CodecOptions() // do we need to add some options? if _, exists := options[constants.OptionKeyClientCallback]; hasCallback && !exists { options[constants.OptionKeyClientCallback] = ctx.QueryValue(CallbackParameter) } output, marshalErr := service.MarshalWithCodec(codec, responseObject, options) if marshalErr != nil { return marshalErr } // use the HTTP responder to respond ctx.HttpResponseWriter().Header().Set("Content-Type", codec.ContentType()) // TODO: test me a.httpResponder.With(ctx, status, output) return nil }
// Respond performs an API response, adding some additional data to // the context's CodecOptions to support our custom codecs. This // particular function is very specifically for use with the // github.com/stretchr/goweb web framework. // // TODO: Move the with={} parameter to options in the mimetypes in the // Accept header. func Respond(ctx context.Context, status int, notifications MessageMap, data interface{}, useFullDomain ...bool) error { body, err := web_request_readers.ParseBody(ctx) if err != nil { return err } if ctx.QueryParams().Has("joins") { if m, ok := body.(objx.Map); ok { m.Set("joins", ctx.QueryValue("joins")) } } protocol := "http" if ctx.HttpRequest().TLS != nil { protocol += "s" } host := ctx.HttpRequest().Host requestDomain := fmt.Sprintf("%s://%s", protocol, host) if status == http.StatusOK { location := "Error: no location present" if locationer, ok := data.(Locationer); ok { location = fmt.Sprintf("%s%s", requestDomain, locationer.Location()) } ctx.HttpResponseWriter().Header().Set("Location", location) if linker, ok := data.(RelatedLinker); ok { linkMap := linker.RelatedLinks() links := make([]string, 0, len(linkMap)+1) links = append(links, fmt.Sprintf(`<%s>; rel="location"`, location)) for rel, link := range linkMap { link := fmt.Sprintf(`<%s%s>; rel="%s"`, requestDomain, link, rel) links = append(links, link) } ctx.HttpResponseWriter().Header().Set("Link", strings.Join(links, ", ")) } } // Transitionary period - don't pass the domain to the codec // unless it's requested in the responder if len(useFullDomain) == 0 || useFullDomain[0] == false { requestDomain = "" } options := ctx.CodecOptions() options.MergeHere(objx.Map{ "status": status, "input_params": body, "notifications": notifications, "domain": requestDomain, }) // Right now, this line is commented out to support our joins // logic. Unfortunately, that means that codecs other than our // custom codecs from this package will not work. Whoops. // data = CreateResponse(data) return goweb.API.WriteResponseObject(ctx, status, data) }
// Responds to the Context with the specified status, data and errors. func (a *GowebAPIResponder) Respond(ctx context.Context, status int, data interface{}, errors []string) error { if data != nil { var dataErr error data, dataErr = codecs.PublicData(data, nil) if dataErr != nil { return dataErr } } // make the standard response object if (a.AlwaysEnvelopResponse && ctx.QueryValue("envelop") != "false") || ctx.QueryValue("envelop") == "true" { sro := map[string]interface{}{ a.StandardFieldStatusKey: status, } if data != nil { sro[a.StandardFieldDataKey] = data } if len(errors) > 0 { sro[a.StandardFieldErrorsKey] = errors } data = sro } // transform the object var transformErr error data, transformErr = a.TransformStandardResponseObject(ctx, data) if transformErr != nil { return transformErr } return a.WriteResponseObject(ctx, status, data) }