rs / rest-layer

REST Layer, Go (golang) REST API framework
http://rest-layer.io
MIT License
1.26k stars 114 forks source link

Bogus error line, when returning empty response on 204 #262

Closed Dragomir-Ivanov closed 4 years ago

Dragomir-Ivanov commented 5 years ago

When Prefer: return=minimal is used, rest-layer will return empty body with HTTP code of 204, however this results in a bogus log line with following text:

Can't send response: http: request method or response status code does not allow body

Mental trace of the issue: func itemPatch executes this statements:

status = 200
    if isNoContent(r) {
        item.Payload = nil
        status = 204
    }

thus making item.Payload = nil However later down the pipeline in func FormatItem(...)(ctx context, body interface{}) we have:

    if skipBody {
        return ctx, nil
    }
    return ctx, i.Payload
}

If skipBody is not flagged, we are returning map[string]interface{}{nil} which is not the same as interface{}{nil}. Then later we have func (s DefaultResponseSender) Send(...)

    w.WriteHeader(status)

    if body != nil {
        j, err := json.Marshal(body)
        if err != nil {
            w.WriteHeader(500)
            logErrorf(ctx, "Can't build response: %v", err)
            msg := fmt.Sprintf("Can't build response: %q", err.Error())
            w.Write([]byte(fmt.Sprintf("{\"code\": 500, \"msg\": \"%s\"}", msg)))
            return
        }
        if _, err = w.Write(j); err != nil {
            logErrorf(ctx, "Can't send response: %v", err)
        }
    }

Which checks the body iterface{}, and it is not nil but map[string]interface{}{nil}, so it proceeds with marshaling it to null string. Then on the w.Write(). But since Go's http package doesn't like we just wrote w.WriteHeader(status) of 204, it fails on that w.Write call with the error string Can't send response: http: request method or response status code does not allow body.

Will make a PR shortly.