Complete Go for Professional Developers

Modifying Request Context

Melkey
Twitch
Complete Go for Professional Developers

Lesson Description

The "Modifying Request Context" Lesson is part of the full, Complete Go for Professional Developers course featured in this preview video. Here's what you'd learn in this lesson:

Melkey creates a middleware package for the application. Middleware can intercept and modify HTTP requests in the application. The Go context package is imported to add values to the request context. Convenience functions for setting and getting the user from the request are also added in this lesson.

Preview
Close

Transcript from the "Modifying Request Context" Lesson

[00:00:00]
>> Melkey: So with that done, we're actually gonna introduce another package into our application, and this is gonna be the middleware package. So if we go into internal, go ahead and just close these down. Go ahead and add another folder called middleware, and then we'll do Middleware.Go. It will be its own standalone package.

[00:00:21]
Go ahead and call this package Middleware, okay? This is gonna have a type user Middleware. It's gonna be a struck, okay? This struct is gonna have a user store field, which gonna be a type store user, store like that, okay? And then here, we need to create two new functions.

[00:00:46]
And these functions are going to be basically injecting and extracting values from the request.body pointer? So HTTP request, it's a pointer. Typically, we've seen it and we've seen that. We've been able to use the R.body object from it. This time, we're gonna be able to basically interject incoming requests through that pointer, using our middleware, and tell it is the person calling this an anonymous user or a user who's logged in?

[00:01:16]
So to do that, we're gonna create two new functions. The first one is going to be set user, okay? These are not gonna be methods on the user middleware. These can just be standalone functions that exist in the middleware package. It's gonna take an HTTP request, right? So that pointer, cuz we're gonna be modifying it.

[00:01:34]
And it's also going to take a user, which is type PointerStore.User, like so.
>> Melkey: And it's gonna respond with HTTP request, okay? Because we're modifying it, and we're going to respond with that modified HTTP request pointer, okay? So here we'll have the context, because what we're going to do is going to be inserting our user into the context property of request, not the body, but context, right?

[00:02:04]
So every request that comes through the point of HTTP request has this property of context. So here we have context. I'm gonna create this using the standard context library from Go. So we need context with value. You should see got brought in context package. And here we're gonna take the context that gets brought from request.

[00:02:26]
So, r.context like so. And here we're gonna put, not like that. But here we're gonna add a new value to the r.context. I'm gonna first add a user key. So first, you may think the first thing you wanna do is add like a user key like so and then user.

[00:02:51]
And this is gonna be completely fine and we can return r.withcontext and pass in context, okay? So this is fine, but what will end up happening with that user key, you're gonna potentially have collisions of different users with the request param, okay? So what you can typically do is you can have a separate type that you define as a context key, which will be a type string, and you can set your user context key as a value user using that key.

[00:03:25]
So here I'll make a new type called context key, which will be string. Okay, but it's its own individual type here and I can do something like const user context key. It is going to be context key, and then we can put in something like user.
>> Speaker 2: Do you expand on the collisions a bit more?

[00:03:53]
I'm not fully understanding.
>> Melkey: So if we have just the string like this, user key. With the way the context package works, you'll end up getting collisions on just the string type. So you have to make like your own custom type, which is still a string, and then you can embed it using something like user or user key.

[00:04:15]
And then instead of putting the user key here, you can just put a user contacts key like so. I didn't mean to do that.
>> Speaker 3: You could have multiple keys that are of type string that are the same string value, and by creating a new type, then when it indexes that key or whatever in context because it's a different type.

[00:04:39]
>> Melkey: Yeah.
>> Speaker 3: That's what differentiates it.
>> Melkey: Yeah, yeah, cuz the different native type behind it. Okay, so we have this set user store again. So this set user function, it's not used right now, but we're gonna create a function off our middleware. And that function is going to be using the set user.

[00:04:56]
And then we don't really need to have this function right now, but what we can do is do func getUser, and it's gonna be kind of the opposite of setUsers. It's gonna retrieve the value here. So this is still gonna take an r, which can be a pointer to HTTP request, okay?

[00:05:14]
And it's gonna return a store.user for us. And what we're gonna do here is user, okay, and here we'll do r.context, okay? We'll get the value associated with context, and here is, we'll put in the user context key to retrieve that value, and we'll see it makes sure that this asserts if the type is in fact Store.User.

[00:05:39]
So this notation of the dot and comparing the types, these assures us that the type that we're retrieving from the context is value is going to be of type, store.user. A pointer to store.user, okay? And so if this is not okay, this is similar to like mapping, like if map exists or not, we can use this okay notation as well.

[00:06:02]
So if it's not okay, so if it's not the same type, we can handle this error. We can bubble it up typically, though we could also, in fact, panic on this as well, right? You can do panic, let's say missing user in request. Because at this point, once we hook this up, we expect every request that's going to be calling getUser is going to have a pointer to store.user.

[00:06:29]
Whether that's anonymous user or whether that's a logged in user. So if something is not there, we could panic. The reason behind the panic is like this: could it be a bad actor call, right? Like a hacker, or something. So we could pack on this. That is a bit of an intense handling of it that basically crashes into our app.

[00:06:58]
So I'll leave it up to you. I will typically use something like panic, honestly. And if anything's going to try to be interjected, like a bad call, or insert something through my application, I'll panic it, or I'll kind of close the HTTP layer. Okay, so now that we have the user, we can just return user like that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now