The Secret HttpClient Feature You Need To Use in .NET

Поделиться
HTML-код
  • Опубликовано: 25 ноя 2024

Комментарии • 152

  • @nickchapsas
    @nickchapsas  Год назад +290

    I accidentally cut the part where I show the HttpClient and DelegatingHandler association! To associate the handler to the client all you need to do is chain an AddHttpMessageHandler call to the AddHttpClient call like this: builder.Services.AddHttpClient("weather").AddHttpMessageHandler();

    • @seantech5358
      @seantech5358 Год назад +14

      Sweet! That makes more sense. Looks sweet too! 😁

    • @TonyBastonHall
      @TonyBastonHall Год назад +12

      Thanks! I thought I'd blinked and missed it!

    • @alexclark6777
      @alexclark6777 Год назад +13

      I was about to replay the video on 0.75x speed to see where I'd missed it, so I'm glad I read the comments first haha.

    • @baetz2
      @baetz2 Год назад +6

      That's the most interesting part

    • @gordug
      @gordug Год назад

      You would think they could register the handler when you map it to the client 😢

  • @ElOroDelTigre
    @ElOroDelTigre Год назад +82

    If the user requests the weather for London you can just return a constant response "The weather is crap" and no need to cache.

    • @Sanabalis
      @Sanabalis Год назад +3

      That's what the real services do anyway.

  • @doublebass120
    @doublebass120 Год назад +42

    For me, I used this to check if I had a bearer token cached for a 3rd party API. If it was cached and not expired, set the authorization header. Otherwise, authenticate with the API and then add the header.

    • @Punkologist
      @Punkologist Год назад +2

      yep this is exactly what we use it for

    • @Workshopshed
      @Workshopshed Год назад +1

      Same here. Checking a token for expiry and call refresh endpoint if expired

    • @yazidarezki3757
      @yazidarezki3757 Год назад

      I do the same with tokens, I have scenario where I am using the _httpClientFactory.CreateClient("MyService") and then I add the Authorization token to the header and If two separate threads are calling _httpClientFactory.CreateClient("MyService") and the fact it will return the same instance. There could issues with this!. To overcome the problem I am using SemaphoreSlim(1,1). My question do I need to make use of SemaphoreSlim.? Thanks

  • @sentenza5497
    @sentenza5497 Год назад +12

    I use those handlers for example to put the bearer token on the Authorization header of the request. A provider injected into the handler takes care of the caching and refreshing process of the token.

  • @askids
    @askids Год назад +14

    If you are using service repository pattern, you would typically do this kind of caching at the service level. That way, if your underlying API provider changes, format changes OR you have a backup API provider for weather, caching still happens at the service level. Caching it at the httpclient's message handler level does not look like an optimal choice for caching. We use message handlers all the time, for injecting tracking headers, missing authorization headers, add client certificates etc. Those are your typical use cases of using custom message handlers.

    • @sentenza5497
      @sentenza5497 Год назад +2

      I feel the same. But if you want to throttle due to the licensing model of the underlying api, it seems legit to me. Nothing domain specific at this point.

    • @okmarshall
      @okmarshall Год назад +2

      @@sentenza5497this is the important point for me, the reason for caching. In this case it's because of cost per request so it makes sense to do a quick caching implementation at the level that's going to hurt.
      As the app grows I'd agree with caching at a higher level, but maybe not as a first pass where you want to get the MVP working and you don't want to hammer the API during dev testing.

  • @ThomasHaxley
    @ThomasHaxley Год назад +2

    I've used this in the past to manage rate limits on API's. One we used only allowed a request every 1 second. So I used a delegate handler to check when the previous request was sent and wait on the next one if required. Worked perfectly.

  • @emerynoel567
    @emerynoel567 Год назад +4

    I did actually know about it! I used it to create a "mock" for use on certain environments, to prevent actually making http calls to any kind of 3rd party api. Very useful for testing and "mirror" environments!

  • @rogeriobarretto
    @rogeriobarretto Год назад +9

    Very useful content. Being dealing with DelegateHandlers lately and they are very flexible.
    This is a neat approach most notably to Mock you responses for UnitTesting.
    Delegating the Caching strategy to the Client seems to be a nice example like you said with minimum code base changes. The beauty of this is that it also helps any fallback logic that relies in your API response error avoiding going to other API if the current API is down or throttled

  • @SvdSinner
    @SvdSinner Год назад +14

    I have to say your timing on this is so perfect for me. I was literally sitting down to start working through how I was going to improve performance on several API calls using caching when I saw this!

  • @kennethswan7853
    @kennethswan7853 Год назад +3

    I’ve used this a ton for getting an auth token (or using a cached/unexpired token) to append to the request authorization header before sending

  • @javiergarciadelanoceda7708
    @javiergarciadelanoceda7708 Год назад +4

    I use it with Refit passing an object that represents the amount of time for each request. Also for body content with a simple interface you could cache it by sending the body as a Option in the HttpRequest

    • @ShahidKhan-vs8bb
      @ShahidKhan-vs8bb Год назад

      I am using refit in a project that i am starting. Could you please share your solution?

    • @javiergarciadelanoceda7708
      @javiergarciadelanoceda7708 Год назад

      @@ShahidKhan-vs8bb I do not have it right here. What I did was that all the request which has a body and I wanted to cache I put the [Body, Property] attributes, and named all the objects body. Example Task GetResponse([Body, Property] Request body, [Property] int cacheTime). The Request object inherits from an interface that has a Method string GetKey().
      With the URL+RequestObject you form the key and with the cacheTime you form the ExpirationTime.
      In the HttpRequest object you can access to both variables ( They are both in the Properties, which is deprecated, and in the Options)

  • @Lammot
    @Lammot Год назад +6

    The feature itself is useful, but generally, for a more simple shared concerns: logging, header injection, stuff like that. Not caching.
    As you've shown, generally you want to cache only successful responses. This and having cache in the handler forces you to:
    - duplicate response validation logic inside the handler and will need to recreate the response with enough information, which is a bloat.
    - wrapping the cached payload in pointless objects which increases the bloat.
    - if you want to cache only certain endpoints, you'll need to add branching logic in that handler. Bloat.
    - won't be able to add typed caching, since it'll force you to add add an extra serialization-deserialision step (to and from content) which is, again, an overhead.
    tldr: better to add cache before the client, for example, as a decorator to a service that handles calling an API.

  • @Fred-yq3fs
    @Fred-yq3fs Год назад +9

    The components of the key might depend on the end-point, so the handler is not quite the right place for the extraction of the components of the caching key. I prefer to use a typed IClient and decorate it. I use delegating handlers for concerns which don't depend on the end-point, such as header manipulations (as you said at the end)

    • @kipsate
      @kipsate Год назад +3

      this pretty much, def not the place for caching

    • @nickchapsas
      @nickchapsas  Год назад +2

      The handler is only used by that specific endpoint so there is no problem with the implementation at all. Nothing else can instantiate it and no other endpoint can be used

  • @W1ese1
    @W1ese1 Год назад +5

    That means that the handler is registered for every HttpClient that will be created so we actually would need a check for the uri so that it's only executed when necessary. At least in your current implementation.
    I guess it would have been good to also showcase the ability to register a handler for a specific HttpClient so that this uri check is not necessary.

    • @nickchapsas
      @nickchapsas  Год назад +1

      That's not correct. The HttpClient is named so only the weather httpclient will have this ability. The handler won't exist for other clients

    • @332glenn
      @332glenn Год назад +11

      @@nickchapsas You register the handler to the application, not directly to the named HttpClient. How does it know that it should only be attached to the named Weather HttpClient.

    • @332glenn
      @332glenn Год назад

      Hmm my last message might've been removed as I added a link to the Microsoft docs,
      You can use the following to add a handler to a specific HttpClient
      builder.Services.AddTransient();
      builder.Services.AddHttpClient("HttpMessageHandler")
      .AddHttpMessageHandler();

    • @PankajNikam
      @PankajNikam Год назад

      +1@@332glenn

    • @seantech5358
      @seantech5358 Год назад

      @@nickchapsas @332glenn and @W1ese1 are right. You do not register the handles specifically for the named instance. Or not that we can spot anyway!

  • @KevinKraemer-c4w
    @KevinKraemer-c4w Год назад

    I’ve used this a lot for mocking out requests in unit tests. Helpful if you are calling a third party api and just want to check success or specific errors

  • @Punkologist
    @Punkologist Год назад

    This is how I get and add the access token to the requests when using an nswag generated client, but I hadn't thought of doing other stuff with it. Never thought of caching at the webclient level. Great video.

  • @RickMcDee
    @RickMcDee Год назад +13

    Maybe I missed something, but since you registered the CachedWeatherHandler with the default DI method, does this mean it is used by all HttpClients in this project?
    Also, if I register multiple handlers, are they used in the same order as I registered them in the Program.cs?

    • @nickchapsas
      @nickchapsas  Год назад +1

      Since the only thing that will instantiate the handlers is the NAMED http client, specific to the weather, it will only be used by the weather client. Regarding handlers, order matters in how they are added to the client, not registered in DI.

    • @krzysztofmilewski9516
      @krzysztofmilewski9516 Год назад

      You can also explicity configure which handlers are used with which clients when registering clients in the DI. Not sure about named clients, but it works for typed clients.

    • @RickMcDee
      @RickMcDee Год назад

      @@nickchapsas Thank you, your pinned comment explained the part that confused me.

  • @leonardomoreno23
    @leonardomoreno23 Год назад

    This handler was extremely helpful for me to implement Auth elegantly before to call an API.

  • @luisbernalcespedes
    @luisbernalcespedes Год назад

    Thank you Nick for sharing the usage of DelegatingHandlers with this particular example. IMHO and in this particular usage, I want to share another approach that I have used before: for caching purposes, I have used a typed HttpClient (instead of a named one) for the API I'm consuming, so that the lookup and write into the cache are both done in such typed client instead - I presume I could have used a HttpClient decorator as well, just like cached repositories are usually implemented. In that way we wouldn't be required to parse the query string or build the HttpMessageResponse and only use any of the corresponding DTO classes instead.

  • @pointoflight2914
    @pointoflight2914 2 месяца назад

    I tried using this approach to log http responses based on some initial configuration. e.g a whitelist of headers to add.
    You have to be careful that if you read the content body as a stream then you can't later consume the same stream. This was important because we were using it as a package to be available to other internal projects and we couldn't predict how the content would be read downstream. Well, we could because it was us using it... but you see what I mean.

  • @RamonDeKlein
    @RamonDeKlein Год назад +1

    It’s also very useful to inject a handler that stubs responses for testing. Implement your own caching is typically a bad idea (especially with HTTP). You should properly deal with caching headers and that’s not trivial. Please use a solid existing caching handler instead of wrapping your own.

  • @evobenim
    @evobenim Год назад +2

    You didn't check api call is success before adding it to cache. I mostly use messagehandlers for logging, security and injecting parameters into the request if it is needed.

  • @KirillKrylovTo
    @KirillKrylovTo Год назад

    This is my default way to
    - mock external responses,
    - add auth token and trigger refresh

  • @MrIsrael3000
    @MrIsrael3000 Год назад +3

    Nice, I’m surprised Polly don’t include client caching, two points I believe could improve the solution:
    - implementation as a generic cache handler, means not specific to this controller.
    - having an status code or out header that indicates the result is from cache for troubleshooting.
    - having a header the cache handler read to avoid the cache.
    What do you think?

    • @aluced
      @aluced Год назад

      A generic cache handler might be tricky to pull off because of the cache key unicity which holds some business logic

    • @Workshopshed
      @Workshopshed Год назад

      Polly can do caching, supports in memory, distributed or redis

  • @krzysztofmilewski9516
    @krzysztofmilewski9516 Год назад +5

    I've been using this feature for a while now, a very elegant way to manage HTTP requests without bloating your logic with repeating code. I just have one questuion - can you elaborate on why did you register your handler with a scoped lifetime? Examples in MS documentation use transient scope with the following explanation:
    "When IHttpClientFactory creates a new delegating handler, it uses DI to fulfill the handler's constructor parameters. IHttpClientFactory creates a separate DI scope for each handler, which can lead to surprising behavior when a handler consumes a scoped service."

    • @Mig440
      @Mig440 Год назад

      If you read the documentation carefully it talks about the using a scoped service inside the handler not the scope of the handler itself 😊

    • @ShawnHepworth
      @ShawnHepworth Год назад

      You must register the handler as transient. It should be registered as transient primarily because it is not thread-safe. If it were registered as scoped, the same instance could be reused across multiple HTTP requests within the same scope, which could lead to unintended side effects, race conditions, or other threading issues.

  • @malcolmchambers4934
    @malcolmchambers4934 Год назад

    I can see this as being useful for a wide range of testing needs

  • @chrisvandewouw
    @chrisvandewouw Год назад

    Very interesting indeed. I'm at the start of a project for implementing a kind of proxy/anti-corruption layer providing a well-structured api and with dependencies to several other webapplications. Preferable I would like to have these webapplications available in a container so I can write integration tests as we do for code that only requires a database, but that is not the case. Using this feature is as close as the next best thing I can imagine. It's basically the same as regular mocking.

  • @KillerMindawg
    @KillerMindawg Год назад

    I use it for http logging.
    I've also used it for load testing where you want to load test services in isolation mode. It can help simulate latencies from different services very easily.

  • @br3nto
    @br3nto Год назад

    5:56 using underscores for fields is like using capitalisation for public/private

  • @kevinlloyd9507
    @kevinlloyd9507 Год назад

    I've used this with Polly for retries on http calls. But I think, since then there's probably something that does this automatically.
    Also for global exception handling. I do custom parsing of the response, then throw an application exception that my app can handle.

  • @KasperDahlTangen
    @KasperDahlTangen Год назад

    Works nice for ratelimiting as well

  • @joelinherts
    @joelinherts Год назад +1

    I remember I used this ages ago to inject custom headers to pretend I was behind a reverse proxy and so the server would authenticate me. I’m sure there are utilities out there now that will do the same without any scripting. Maybe fiddler.

  • @neralem
    @neralem Год назад

    I think for london you can statically return "Rainy".

  • @Paps526
    @Paps526 Год назад +2

    I think you missed the part where you should wrap your handler over defaultHttpHandler of HttpClient

    • @nickchapsas
      @nickchapsas  Год назад +1

      There is no wrapping. I accidentally cut the registration part. Check the pinned comment

    • @Paps526
      @Paps526 Год назад

      @@nickchapsas Yea that's right, but in a nutshell it's like wrapping, your handler wraps around inner one

  • @paul99782
    @paul99782 Год назад +2

    Hey Nick! Love your content, especially about Web API. I was wondering if you'd ever do videos on authentication between clients and .Net 6+ Web API? Specifically, authentication using a token rather than using user credentials. I find that most of the apps I see created internally for businesses don't warrant the use of full-fledged user credential authentication. Didn't know if you had suggestions for this route or even videos out/in the pipeline?

    • @vacc1001
      @vacc1001 Год назад

      I just created a bare bones JWT token generator and verification library that's shared between projects. Barely a handful number of simple methods. Has been running smoothly for 5 years and counting. Might want to go down that route.

  • @jackp3303
    @jackp3303 Год назад +1

    May I ask one thing - what's are advantages of this way of caching in comparison with standard (let's say use some service with the same InMemoryCache)?
    For me HttpClient should do 1 thing - send requests and receive response, that's it's responsibility and it doing it well, but changing it's pipeline, adding some caching possible may produce some "hard to debug" \ "understand the code" issues, especially for beginners.
    I also don't see any perf advantages, similar thing.
    + More difficult configuration - so if we have many APIs in our microservice, each service need specific configuration of HttpClient. Having specific MyServiceNameCache service, injecting it into MyServiceName and just before the call of httpClient check the cache is much easy to read and debug.
    Or I lost the idea?

  • @sospam3417
    @sospam3417 Год назад

    Mentioning ValueTask when using a cache would have been nice. Otherwise great video!

  • @IvanAndreev91
    @IvanAndreev91 Год назад

    thanks for video! I enjoed with it!

  • @athanasmb
    @athanasmb Год назад

    thanks Nick!

  • @jeremybray8050
    @jeremybray8050 Год назад +1

    Not sure if its a big deal to you but you showed your API Key at 2:10.

    • @maskettaman1488
      @maskettaman1488 Год назад +1

      If the past is any indicator, that API key will be deactivated before the video goes live

  • @MatthewCrawford
    @MatthewCrawford Год назад

    I keep an extension class on hand for that, generic lazy loading cache helper (AddOrGetExistingCacheEntry) to use as localized cache for several internal processes.
    Have they added something like that to the basic system memorycache?

  • @ttolst
    @ttolst Год назад

    I really wish we would stop adding so much hidden indirection, making reading and debugging the code much harder than it need to be.
    In so many places, proper seperation of logic and IO will make things like DI and mocs not needed, but if i could choose to kill just one thing, it would be automapper - the destroyer of code navigation and turning compile errors into runtime errors.

  • @ShawnHepworth
    @ShawnHepworth Год назад

    The delegating handler has to be registered as transient. Not scoped. Look into it a bit more. It should be registered as transient primarily because it is not thread-safe. If it were registered as scoped, the same instance could be reused across multiple HTTP requests within the same scope, which could lead to unintended side effects, race conditions, or other threading issues.

    • @BlTemplar
      @BlTemplar Год назад

      It can be registered as a singleton if the code you put in it is thread-safe.

    • @nickchapsas
      @nickchapsas  Год назад

      Transient is the general recommendation by Microsoft because it’s the safest option but it’s not a "has to" thing. In this case for example there will be no scenario where scoped causes unexpected behaviour

  • @terjes64
    @terjes64 Год назад

    If this vid came out one week earlier I would implement this, and not make a manual cache... Oh well there is always next time..

  • @Meir017
    @Meir017 Год назад

    Isn't there an issue where you can read the response.Content only once or does that happen only if you wrap the content in a using block?

  • @hakienet3897
    @hakienet3897 Год назад +1

    Why would you make a custom handler instead of just caching the response from the api?

    • @jamesreynolds3167
      @jamesreynolds3167 Год назад

      Maybe you might have 2 different sets of business logic that do different things, but still depend on the same api.

    • @nickchapsas
      @nickchapsas  Год назад

      What James said. Because it's a differnet layer of caching specific to the single API. You might not want that for the complete API response especially for things like auth

  • @pramod.kulkarni9607
    @pramod.kulkarni9607 Год назад

    Can i use this iin tge multi tenent architecture

  • @teisdraiby9386
    @teisdraiby9386 Год назад

    Is there any way the handle could pass custom information to the caller? For example whether the response comes from cache or the the actual api?

    • @krb3d
      @krb3d Год назад

      Just add custom header for cached cases or something like this - you are in full control there.

  • @majidmajid2192
    @majidmajid2192 Год назад

    Why we need to use this methods and classes and not cache in our service layer?

  • @DanielAWhite27
    @DanielAWhite27 Год назад

    I did the same thing for bearer token management.

  • @Autystyczny
    @Autystyczny Год назад +1

    Isn't this an overengineering? You can add caching to the method that make the call to the API and that does the same thing without adding extra layer of classes

    • @nickchapsas
      @nickchapsas  Год назад +8

      Does that class need to know about the caching? I would argue it doesn't. Same with something like adding retry policies or fallbacks. It's not the responsibility of that class to know that IMO but it heavily depends on context as well

    • @marcusvmgg
      @marcusvmgg Год назад

      great point. That's the usual implementation we do, usually we create a class responsible for dealing with weather data (CacheWeather), and that class would be the one with the logic for caching or httping it and later return to the service class

    • @JerryFederspiel
      @JerryFederspiel Год назад

      I can't help but feel like all of this could've been made simpler if some of the types related to HttpClient used interfaces so we could transparently decorate them with caches/retries if we wanted to.

  • @adityadesai7263
    @adityadesai7263 8 месяцев назад

    every time I make a HTTPS post call with httpclient class, Httpclient response is very slow. any way to speed it up. Mean while post request to http is fast, its https that is causing issue

  • @parlor3115
    @parlor3115 Год назад

    Caching should be done at the service level not the http client level. Like this, you're caching any request (weather API or not) plus other issues too.

    • @nickchapsas
      @nickchapsas  Год назад

      No you’re not. This handler is only used by the weather api and only caches for the weather api

    • @parlor3115
      @parlor3115 Год назад

      @@nickchapsas You're right. I missed the named client part, but still, you don't control what the handler caches unless you write code that checks the content of the request and it can even run on non GET requests.

    • @skwas6012
      @skwas6012 Год назад

      @@parlor3115 I agree with you. Caching of HTTP content should be based on HTTP cache control, etag, last modified and so on. This is what could be handled at the HTTP client side in a DelegatingHandler (or a reverse proxy cache). If an upstream service however does not provide HTTP level caching, and/or you do not have a reverse proxy cache, by all means introduce cache in your application if you need to have this data frequently, but keep it in the domain/service using a service decorator pattern or something alike.

  • @timmkrause6684
    @timmkrause6684 Год назад

    Anyone knows the screen magnyfier Amichai is using?

    • @birolaydin4731
      @birolaydin4731 Год назад

      ZoomIt

    • @timmkrause6684
      @timmkrause6684 Год назад

      @@birolaydin4731 ZoomIt does not support these rainbow colors. He‘s using macOS with Presentify in that case.

  • @DaminGamerMC
    @DaminGamerMC Год назад

    Do the courses give certificates?

  • @meowzhin
    @meowzhin Год назад

    That's Refit, with extra steps and less features.

  • @foundev
    @foundev Год назад

    Or you can just create an extension method so that future developers don't have to go through all the documentation to understand this hidden functionality. xD Nice video tho!

  • @sergeitishkov2385
    @sergeitishkov2385 Год назад

    The approach looks clever but I don't like it. It increases the amount of magic in the app. A new dev who comes there after 3 years might be killed by this clever stuff when the app behaves not as expected. It would be much better to implement such a feature even with an abstract class despite the fact that abstract classes rarely a good solution. But here even an abstract class would be much better.

  • @nataliasikorka4825
    @nataliasikorka4825 Год назад

    Just a friendly reminder that IMemoryCache is not thread safe, and could be choke point if impelemted the way Nick showed in this video.
    Nick has even video about that 😄:
    ruclips.net/video/Q3KzZeUudsg/видео.htmlsi=4HhjlX6bUbEHfV2R

  • @slipoch6635
    @slipoch6635 Год назад

    you flash your API key

    • @nickchapsas
      @nickchapsas  Год назад +1

      Go on, use it

    • @slipoch6635
      @slipoch6635 Год назад

      laughed my arse off when I saw this comment ;) @@nickchapsas

  • @boogyman2501
    @boogyman2501 Год назад +4

    Love your videos, have been a big fan for a while now but profanity is not cool my dude

  • @margosdesarian
    @margosdesarian Год назад

    Ha ha ha ha ha ... "In London, a lot" ..

  • @Pleviwow
    @Pleviwow Год назад

    calling http handlers a secret feature is a bit of an overstatement

  • @MatinDevs
    @MatinDevs 11 месяцев назад

    Imagine using it in a company's project 😂

  • @noijze
    @noijze Год назад

    If it is "secret" it is poorly documented bij MS

  • @oleksandr.liakhevych
    @oleksandr.liakhevych Год назад

    Those click-bait titles Nick uses are really frustrating.
    I understand why they are used, but anyway

  • @dewaldabrie
    @dewaldabrie Год назад +1

    Interesting topic, but no need to take the name of Jesus Christ in vain (around 3:25).

  • @johnny-vz
    @johnny-vz Год назад

    Man, I was left with a bad taste in my mouth when I started watching this. You don't even have the guts to own your slip of tongue. Sorry if it was intentional, You lost me.

  • @bd73ne
    @bd73ne Год назад

    Love your videos but please don’t use profanity.

  • @nepalxplorer
    @nepalxplorer Год назад +1

    First

  • @4arliEdinorog
    @4arliEdinorog Год назад

    Охуенно