aohorodnyk / mimeheader

Library to work with MimeHeaders and other mime types. Library supports wildcards and parameters.
https://aohorodnyk.com/post/2021-07-03-mime-headers/
MIT License
24 stars 3 forks source link

#10 Export AcceptHeader.MHeaders #11

Closed Jake-Convictional closed 2 years ago

Jake-Convictional commented 2 years ago

Closes #10

This change makes AcceptHeaders.MHeaders an exported property so that users of the package can take advantage of the HTTP-compliant parsing and make use of the results in a flexible manner.

For example, I want to match a media type in the Accept header using a custom regex on the SubType rather than an exact match.

New tests not applicable in this case.

aohorodnyk commented 2 years ago

@Jake-Convictional I appreciate for your contribution. I'm ok to expose MHeaders, however I would like to to discuss your use case to try to find the best solution.

Could you write your use case in details, please?

Jake-Convictional commented 2 years ago

No problem!

I'm implementing GitHub-style versioning that uses the Accept header to specify the API version being requested. It would typically look something like: Accept: application/vnd.mycompany.2022-01-01+json.

To enable that, I want to be able to properly parse an Accept header according to the HTTP spec (your library solves that), and then I want to do the following:

aohorodnyk commented 2 years ago

@Jake-Convictional Hm, you have pretty interesting task. I though about some flexible configurations.

Follow to whatever I see in GitHub documentation, I can assume that we have known list of supported versions and response types. At least we can generate it and get it during the HTTP middleware.

Before execution we will generate multiple supported mime types, like:

  1. application/vnd.mycompany.2022-01-01+json
  2. application/vnd.mycompany.2022-01-01+xml
  3. application/vnd.mycompany.2022-03-01+json
  4. application/vnd.mycompany.2022-03-01+xml

In handler we do something like:

    supported := []string{"application/vnd.mycompany.2022-01-01+json", "application/vnd.mycompany.2022-01-01+xml", "application/vnd.mycompany.2022-03-01+json", "application/vnd.mycompany.2022-03-01+xml"}
    ah := r.Header.Values("Accept")
    found := false
    var fh MimeHaeader
    for _, v := range ah {
        ah := mimeheader.ParseAcceptHeader(v)
        h, _, m := ah.Negotiate(supported, v)
        if m != nil {
            if found {
                // Two supported headers found, error.
                return
            }

            found = true
            fh = h
        }
    }

    // Parse the Accept header and use it as you wish.

@Jake-Convictional probably I'm missing something, but I would like to understand the improvements I can do to the library based on your use case. Is it possible to ask you, please, to provide an example, how the exposed MHeaders will help you in your task?

Thanks a lot!

aohorodnyk commented 2 years ago

@Jake-Convictional I'm also thinking about adding wildcard search support, like vnd.mycompany.*. But I'm wondering how useful is it?

Also, I would like to add that GitHub-style versioning looks pretty interesting to me.

Jake-Convictional commented 2 years ago

Wow thanks for taking the time to put that together! That almost does what I need except for a few things which I don't think would be possible using the package:

So my preferred way to do this would be:

supported := []string{"2022-01-01", "2022-03-01"}
ah := r.Header.Values("Accept")
found := false
version := ""
var fh MimeHeader
for _, v := range ah {
        ah := mimeheader.ParseAcceptHeader(v)
        for _, mediaType := range ah.MHeaders {
                if mediaType.Type == "application" && strings.HasPrefix(mediaType.SubType, "vnd.mycompany.") {
                        if found {
                                // Two supported headers found, error.
                        return
                        }
                        found = true
                        version = MyRegexToGetTheVersion(mediaType.SubType)
                }
        }
}

if found {
        if !Contains(supported, version) {
                // Bad version, error.
                return
        }
        // Do something with the version now
}

In the end, I think AcceptHeader.MHeaders is a great property to export because every piece of information contained is meaningful/valuable to the caller. MimeHeader is an exported struct already so it wouldn't expose any unnecessary internals.

aohorodnyk commented 2 years ago

@Jake-Convictional Thanks a lot for your example. If you would like to go this route, then exposed MHandler has a lot of sense. I'm merging the PR.

Anyway, if you have an idea how to make library more flexible for your task, I'd happy to discuss.

My thoughts are going around comparable interface that can help to extend flexibility.

Thanks a lot for your contribution!