Re: [grpc-io] Accept-Language in grpc and grpc-gateway

32 views
Skip to first unread message

Eric Anderson

unread,
Nov 16, 2018, 1:24:06 PM11/16/18
to ma...@wardle.org, grp...@googlegroups.com
It's not outrageous to read HTTP standard headers from gRPC. I'm not super-well versed with grpc-gateway, but I'm not seeing any configuration for this supported by grpc-gateway. (SystemParameters may be a solution, but it doesn't seem supported by grpc-gateway and is unclear if it is the "right" solution.)

So I guess either just read from Accept-Language directly or add the "string language_preferences=2" and read from Accept-Language if language_preferences is empty.

On Mon, Nov 12, 2018 at 5:28 AM Mark Wardle <ma...@wardle.org> wrote:

Dear all,

I have a gRPC service that provides results that can be localized. I am using gRPC as well as the grpc-gateway to automatically generate a REST service running in parallel.

The usual way of specifying locale preferences in REST services is via Accept-Language tags. As this is, strictly speaking, part of the API contract, does it make sense to write a gRPC service description that explicitly documents this parameter. I could therefore easily add a "string language_preferences=2" to the request message definition? If so, is there a clever way to populate this in the grpc-gateway much like URL parameters?

Or is there a better way of handling language preferences in grpc and grpc-gateway?

Many thanks,

Mark

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/8033c8e1-2516-40b2-889d-60885ca77419%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mark Wardle

unread,
Jan 7, 2019, 8:49:14 AM1/7/19
to grpc.io
Dear Eric,

Thank you so much for your reply, and apologies for not replying in turn. It was one of those situations in which I posted a question and then in the process of actually formulating the question, I solved the question. I thought I'd deleted my post before anyone saw it!

For posterity's sake, I did what you suggested. For the gateway, we have to ensure the header is passed through, so add an incoming header matcher to the mux:

addr := fmt.Sprintf(":%d", opts.RESTPort)
    dialOpts := []grpc.DialOption{grpc.WithInsecure()} // TODO:use better options
    mux := runtime.NewServeMux(runtime.WithIncomingHeaderMatcher(headerMatcher))


where headerMatcher func is defined as:

// ensures GRPC gateway passes through the standard HTTP header Accept-Language as "accept-language"
// rather than munging the name prefixed with grpcgateway.
// delegates to default implementation for other headers.
func headerMatcher(headerName string) (mdName string, ok bool) {
    if headerName == "Accept-Language" {
        return "accept-language", true
    }
    return runtime.DefaultHeaderMatcher(headerName)
}


And then in my grpc-server implementation, I have this helper func:

// determine preferred language tags from the context, or fallback to a reasonable default
func (ss *coreServer) languageTags(ctx context.Context) ([]language.Tag, error) {
    if md, ok := metadata.FromIncomingContext(ctx); ok {
        preferred := md.Get("accept-language")
        if len(preferred) > 0 {
            pref := strings.Join(preferred, ";")
            tags, _, err := language.ParseAcceptLanguage(pref)
            if err != nil {
                return nil, status.Errorf(codes.InvalidArgument, "invalid accept-language header: %s", err)
            }
            return tags, nil
        }
    }
    return ss.lang, nil
}




which I use like this in any endpoint it is needed:

func (ss *coreServer) GetExtendedConcept(ctx context.Context, conceptID *snomed.SctID) (*snomed.ExtendedConcept, error) {
    tags, err := ss.languageTags(ctx)
    if err != nil {
        return nil, err
    }
    return ss.svc.ExtendedConcept(conceptID.Identifier, tags)
}

Best wishes,

Mark
Reply all
Reply to author
Forward
0 new messages