Refit: Support for getting response headers

Created on 30 Dec 2013  路  25Comments  路  Source: reactiveui/refit

(This issue is at the request of @paulcbetts over twitter, in which I stated I'm willing to give a pull req a try, but some discussion on scenarios first.)

I would like to see support for getting response headers out of the remote api call while still using a strongly typed response (or none at all). This would be for making calls against APIs (ex the azure management api) that return a unique request-id as a response header, which can be used in support cases when something unexpected/bad happens, or more frequently in the case of asynchronous calls in which a call is made against the api such as swapping an azure deployment, in order to take that returned request-id and query its status in a future api call.

Given the way refit is exposed to it's user, the most natural way I feel would be to use attributes i.e.

``` C#
// async api that returns no body
[Post("/some/asyncapi/{someId}")]
Task SomeAsyncRemoteApiCall(int someId, [ResponseHeader("x-request-id")] out string requestId);

// async api that returns body and requestid header
[Post("/some/asyncapi/{someId}")]
Task SomeAsyncRemoteApiCall(int someId, [ResponseHeader("x-request-id")] out string requestId);

I suppose that an alternative to the first one could be

``` C#
[Post("/some/asyncapi/{someId}")]
[return: ResponseHeader("x-request-id")]
Task<string> SomeAsyncRemoteApiCall(int someId);

where the returned Task is for the header, not the string content because the request doesn't return a body. But that seems a little wonky IMO.

Thoughts/comments/insults?

enhancement outdated

Most helpful comment

I think I'd prefer just wrapping my response object in a Refit type, and have Refit do the usual magic for my object as well as include the header info in the Refit type. So something like

class RefitResponse<T>
{
    public HttpResponseHeaders Headers { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    // ... whatever else ...  
    public T Content { get; set;}
}

Then you could have something like this in your Refit interface:

[Post("/some/asyncapi/{someId}")]
Task<RefitResponse<SomeResponse>> SomeAsyncRemoteApiCall(int someId);

and then you can access the header info via Task.Result.Headers

All 25 comments

"This would be for making calls against APIs (ex the azure management api) that return a unique request-id as a response header, which can be used in support cases when something unexpected/bad happens"

So, that's one use-case

"in the case of asynchronous calls in which a call is made against the api such as swapping an azure deployment, in order to take that returned request-id and query its status in a future api call."

And that's a different one.

For the first one, I'd actually rather teach Refit how to translate types it doesn't recognize. What if your SomeResponse had a "Request-Id" property, and you provided a Func<HttpResponseMessage, SomeResponse>?

For the 2nd one, I'd probably rather have a way to explicitly implement _some_ of the API methods and let Refit implement the others. Something like, if you supply an abstract base class, Refit will subclass it and implement all of the ones you don't do

Well the main idea is getting the header from the response, whether or not the call returns a body or not. Having a simple one-property response model to capture the header or adding it to an existing response model would both be fine personally if refit was intelligent enough to map that response header to that property.

I would hate to start having to pass funcs around. The whole idea of refit is this automagic-proxying it-just-works experience right? Seems a bit off if you now suddenly have to fill in the gaps because it requires you to tell it what to do with response headers and pass in a func. Like how you model the calls now - you tell it what to do, but not how to do it, if that makes any sense.

So this is almost a year old now. Is this still an issue with the latest changes? Is there a verdict on how this can be returned?

IMHO, an out parameter seems like it would fit in the best here.

I actually tried to build it but I discovered that out parameters and async don't like each other. One thing I didn't try was using a Task for the value of the out parameter though - maybe that will work?

Hmm...right, I forgot about that.
void Foobar ([header] out Task<Foo> blah);
Is really nasty because it's easy to not await it and there's no compiler warnings.

Yeah, the other option is to provide a wrapper type e.g. ApiResponse<T> with similar properties to ApiException, and maybe even an implicit cast to the result type T. :smiling_imp:

Here's an idea...what if we have a special headers collection that can be optionally passed in and then filled out?
So

class ReturnedHeadersCollection : Dictionary<string,string> {} ...

var headers = ReturnedHeadersCollection.CreateRequest("header1", "header2", ...); // returns a dictionary with the keys and null values.

Task<SomeReturn> DoSomething(string foo, ReturnedHeadersCollection headers);

// extract the value
Debug.WriteLine(headers["header1"]);

Having ApiResponse<T> could work too and be more generic. It could expose the entire httpresponse message too.

Yeah, I'm open to either approach. I think having a wrapper for the response would be more flexible. It would solve a few more problems than just headers -- status code is another obvious bit of useful info we sort of throw away at the moment.

Agreed. In fact, if we return status code as part of ApiResponse<T>, then I think it should have similar semantics to HttpResponseMessaege

  • don't throw if IsSuccessStatusCode is false
  • do check and throw if user tries to access the strongly typed .Content (or implicit cast if we add that)

Sounds good to me. You all good with it, @paulcbetts?

Any update on this? I'm in need of retrieving headers for a project, where a CSRF Token is returned via the header in the response.

I would be too interested to have the status code.

The latest code in master supports returning an HttpResponseMessage object and that would contain the headers.
https://github.com/paulcbetts/refit/blob/master/Refit/RequestBuilderImplementation.cs#L318

Fairly sure that's been there since v1.x. Working with HttpResponseMessage is a decent workaround at the moment, but I think it would be nice to allow returning something with a bit more of a stripped down interface. I'll look at this when I next have some time.

Good to know. It could be added to the documentation (no matter how small it is right now), so that others who want to work with received headers.

Does that has been implemented since?

I would like get the header responses , how can we do?

@auriou one way is to set the return type of your call to Task<HttpResponseMessage> then you could dig out the headers you want.

Yes I used it to do on a single method.

But after I needed to recover the headers Rest at each call.

This is not very convenient and a lot of code has to be written.

To avoid this I have set up a specific HttpClientHandler to be notified of the change of headers.

here is my code.

One Test

[TestMethod]
public void HeaderSimpleTest()
{
var headers = new Dictionary();
var ServerUrl = "http://localhost:26687/Service1.svc";
var handler = new HttpClientHeaderHandler
{
UpdateHeaders = dictionary => { headers = dictionary; }
};

        var client = new HttpClient(handler) { BaseAddress = new Uri(ServerUrl) };
        var restService = RestService.For<IService>(client);
        var resultBonjour = restService.Bonjour().Result;
    }

My Handler

public class HttpClientHeaderHandler : HttpClientHandler
{
public Action> UpdateHeaders { get; set; }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var task = base.SendAsync(request, cancellationToken);
        var result = task.Result;
        if (UpdateHeaders != null)
        {
            var headers = result.Headers.ToDictionary(p => p.Key, p => string.Join(";", p.Value));
            UpdateHeaders(headers);
        }
        return task;
    }
}

My interface

public interface IService
{
    [Post("/Bonjour")]
    Task<string> Bonjour();

    [Post("/Connexion")]
    Task<authentRequest> Connexion([Body] authentRequest request);
}

I'm also very interested in an easier way to access response headers.

I think I'd prefer just wrapping my response object in a Refit type, and have Refit do the usual magic for my object as well as include the header info in the Refit type. So something like

class RefitResponse<T>
{
    public HttpResponseHeaders Headers { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    // ... whatever else ...  
    public T Content { get; set;}
}

Then you could have something like this in your Refit interface:

[Post("/some/asyncapi/{someId}")]
Task<RefitResponse<SomeResponse>> SomeAsyncRemoteApiCall(int someId);

and then you can access the header info via Task.Result.Headers

Any news on this issue?
My use case is simpler than that: I want to be able to distinguish a 200 OK from a 201 Created of a PUT request... and still let Refit do its magic stuff on my objects.
I've searched throughout all issues and didn't find any relevant info apart from this thread.
Any other solution than using Task<HttpResponseMessage>which will bypass all Refit magic?
Thanks

Fixed in #414

An update to the documentation on how to use the ApiResponse<T> would be really useful as it took me a lot of searching to find this :)

I assume Refit doesn't throw exceptions on non-success HTTP Status Codes?

What's the current status of this? I have paging information being return in the response header that I need access to.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ahmedalejo picture ahmedalejo  路  6Comments

hiraldesai picture hiraldesai  路  4Comments

hankovich picture hankovich  路  4Comments

martincostello picture martincostello  路  4Comments

sahinyyurt picture sahinyyurt  路  5Comments