Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom http response headers #1165

Open
wants to merge 13 commits into
base: development
Choose a base branch
from

Conversation

rigved-telang
Copy link

Description:

Additional Information:

  • Usage:
headers := map[string]string{
	"X-Custom-Header":  "CustomValue",
	"X-Another-Header": "AnotherValue",
}

return response.Response{
	Data:    "Hello World from new Server",
	Headers: headers,
}, nil

Checklist:

  • I have formatted my code using goimport and golangci-lint.
  • All new code is covered by unit tests.
  • This PR does not decrease the overall code coverage.
  • I have reviewed the code comments and documentation for clarity.

pkg/gofr/handler_test.go Outdated Show resolved Hide resolved
pkg/gofr/http/response/response.go Outdated Show resolved Hide resolved
go.sum Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to update the go.sum?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None. When I ran go install golang.org/x/tools/cmd/goimports@latest, it updated the go.sum, I can. remove it if you think it is necessary.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, in my opinion, go.mod & go.sum should only be updated if there are dependency update that are adding some value to the feature/enhancement/bug fix in the PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Will remove the additions in go.sum.

// **Handle Custom Headers** if `result` is a `Response`.
if resp, ok := result.(response.Response); ok {
for key, value := range resp.Headers {
w.Header().Set(key, value)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add logic to skip headers that gofr already sets, such as 'Content-type'?

Copy link
Author

@rigved-telang rigved-telang Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if resp, ok := result.(response.Response); ok {
	for key, value := range resp.Headers {
		if w.Header().Get(key) == "" {
			w.Header().Set(key, value)
		}
	}
	result = resp.Data
}

How about this, we'll check if a Header has been set and then set it only if it hasn't been set.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes maybe this can help

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Umang01-hash @aryan-mehrotra-zs @vipul-rawat How about having a list of non overridable response headers (or response headers that can only be overridden by some special methods provided by Gofr)? So that a check can be implemented for those headers only. Intention behind is that, user deliberately wants to override those headers (such as content-type) but since Gofr is internally setting those headers, so the overall working of the framework should not break.

PS: If required, this can be done as future enhancement. For now, this PR is good.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest if the user has provided some header which is already set by gofr that should also be over-ridden, if user doesn't set - the default which is set by gofr is used.

@rigved-telang
Copy link
Author

rigved-telang commented Oct 31, 2024

@ccoVeille @aryanmehrotra @Umang01-hash @shashank-zopsmart I have extracted logError and handleWebSocketUpgrade to reduce the Cognitive Complexity error which was being raised by codeclimate. Kindly review that as well.

case <-panicked:
err = gofrHTTP.ErrorPanicRecovery{}
}

// **Handle Custom Headers** if `result` is a `Response`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure about the formatting here

In a godoc, I would have understand

Here I don't get what it brings.

I might not have raised this, but I also face a misunderstanding about the fact I don't get why there is a need to use emphasis on this

I could have understood if it was

Suggested change
// **Handle Custom Headers** if `result` is a `Response`.
// handle **Custom Headers** if `result` is a `Response`.

But then, I would be lost anyway. Why would we need to emphasis on the fact it's custom headers, maybe you could explain

And finally, why using emphasis and not simply use quote.

Suggested change
// **Handle Custom Headers** if `result` is a `Response`.
// handle "Custom Headers" if `result` is a `Response`.

Thanks for reading this almost useless feedback about a comment 😅😬😂

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ccoVeille there's no particular reason for the formatting, I am new to Go and hence was taking ChatGPT's help while writing the code, it put the comment, I didn't really think about the way the comment was formatted. I will simplify it to just read //handle custom headers if 'result' is 'Response'

Comment on lines +100 to +107
if resp, ok := result.(response.Response); ok {
for key, value := range resp.Headers {
if w.Header().Get(key) == "" {
w.Header().Set(key, value)
}
}
result = resp.Data
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment explaining the reason behind this.

Here is my understanding:

There is a need to propagate the header to the caller.

Then explain existing headers are not overwritten.

For this exact reason, I would create a method in

Suggested change
if resp, ok := result.(response.Response); ok {
for key, value := range resp.Headers {
if w.Header().Get(key) == "" {
w.Header().Set(key, value)
}
}
result = resp.Data
}
if resp, ok := result.(response.Response); ok {
resp.PropagateHeaders(w)
result = resp.Data
}

With something like this in response.Response struct

func(response.Response resp) PropagateHeaders(w *http.Response)
	for key, value := range resp.Headers {
		if w.Header().Get(key) != "" {
			// do not overwrite existing header
			continue
		}
		w.Header().Set(key, value)
	}
}

I wrote this on a phone, there might be some errors 😅

Copy link
Author

@rigved-telang rigved-telang Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this approach is better. I just think the function should be called SetCustomHeaders instead of PropogateHeaders. That would explain the purpose better. Let me know what you think.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shashank-zopsmart @Umang01-hash what do you guys think, should we extract the setting of custom headers to another function or a simple if condition should suffice in the ServerHTTP func itself

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rigved-telang extracting the setting of custom headers to another function is not a bad idea as it would lead to a more clear code and auto explain what the function is intended to do

Comment on lines +153 to +156
if err != nil {
errorLog := &ErrorLogEntry{TraceID: traceID, Error: err.Error()}
h.container.Logger.Error(errorLog)
}
Copy link
Contributor

@ccoVeille ccoVeille Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err != nil {
errorLog := &ErrorLogEntry{TraceID: traceID, Error: err.Error()}
h.container.Logger.Error(errorLog)
}
if err == nil {
return
}
errorLog := &ErrorLogEntry{TraceID: traceID, Error: err.Error()}
h.container.Logger.Error(errorLog)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just extracted the existing code to a new function, also isn't the logic wrong here, the error should get logged when it is not nil right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I edited my post it should be OK now

testCases := []struct {
desc string
method string
data interface{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data interface{}
data any

@Umang01-hash
Copy link
Contributor

@rigved-telang can you please also resolve this linter ?

Screenshot 2024-11-05 at 3 57 05 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

how to return custom http response data and header
5 participants