We know how to convert objects to and from JSON. We know how to send HTTP POST requests. We've written the /login endpoint for the API. We can get a JWT. Now we have to write code to work with an endpoint that uses it.

I want to write the code for /search/series. It seems relatively easy, and its response object isn't very complicated. I'll have to write a little bit more code to support this at each layer. But first let's talk about the endpoint.

It is a GET function that takes a name, imdbId, or zap2itId and, optionally, a language. It will search for a series using these parameters and return results in the indicated language, if they exist. It returns status 404 if the search turns up no results. For simplicity, I'm only going to implement the 'name' parameter.

We don't have code to send a GET request yet. They behave a little different from POST requests. You don't send a message body along with these, so if they take any parameters they are specified via the URL, and you need to perform 'URL encoding' on them. Also, this request has to take a JWT. So we've got our work cut out for us.

First, let's make some code to help build a URL with parameters. Newer HTTP libraries in .NET do this for you, but HttpWebRequest offers no help. A URL with GET parameters looks like this:
Code:
http://example.com?firstParam=asdf&secondParam=hello%20world
That %20? That stands for a space. There are a lot of characters not valid in URLs, and they have special codes for representation. The URI class has EscapeUriString() and EscapeDataString() to help, but you have to be careful which one you use. Instead of spending a day explaining, I'm just going to show you the right way. Add this to RestClient.vb:
Code:
Private Function CreateEncodedParameterizedUrl(ByVal baseUrl As String, ByVal parameters As Dictionary(Of String, String)) As String
    Dim urlBuilder As New Text.StringBuilder()
    urlBuilder.Append(baseUrl)

    Dim parameterIndex As Integer = 0
    urlBuilder.Append("?")
    For i As Integer = 0 To parameters.Count() - 1
        Dim key As String = parameters.Keys(i)
        Dim value As String = parameters(key)
        Dim encodedValue As String = Uri.EscapeDataString(value)

        urlBuilder.AppendFormat("{0}={1}", key, encodedValue)

        If i < parameters.Count - 1 Then
            urlBuilder.Append("&")
        End If
    Next

    Return urlBuilder.ToString()
End Function
It's a little bit of a mess. This is the kind of code I like to make MS write for me. There's some newer types that do, but alas, I've already started down the road using HttpWebRequest. The rules are that the first parameter is of the format "?name=value". Every parameter after that is "&name=value". Technically every name and value should be encoded, but I've never seen someone use a parameter name that had special characters. So I'm just encoding the values. Why don't I build the whole thing, then encode it? Because the "://" after "http" would get encoded too! I told you it was tricky.

Anyway, if you look at that code you'll see it one-by-one appends the right start character, then each name/value pair with the value encoded. Now we can build a GET request that uses a JWT. It reuses some code from POST, for the sake of brevity I'll live with duplication. Add this to RestClient.vb:
Code:
Public Function GetWithJwt(ByVal url As String, ByVal jwt As String, Optional ByVal parameters As Dictionary(Of String, String) = Nothing) As String
    Dim encodedUrl As String = url
    If parameters IsNot Nothing Then
        encodedUrl = CreateEncodedParameterizedUrl(url, parameters)
    End If

    ' Configure the request headers.
    Dim request As HttpWebRequest = CType(HttpWebRequest.Create(url), HttpWebRequest)
    request.Method = "GET"

    ' Build the JWT string and add the right header.
    Dim jwtHeaderValue As String = String.Format("Bearer {0}", jwt)
    request.Headers.Add(HttpRequestHeader.Authorization, jwtHeaderValue)

    ' Read the response and its JSON payload.
    Using response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
        If response.StatusCode <> HttpStatusCode.OK Then
            ' This needs to be more sophisticated, you can flesh it out later.
            Throw New ApplicationException("There was an error with the request.")
        End If

        Using responseStream As Stream = response.GetResponseStream()
            Using reader As New StreamReader(responseStream, Text.Encoding.UTF8)
                Return reader.ReadToEnd()
            End Using
        End Using
    End Using
End Function
Let's pay attention to how it compared to the Rest() method already implemented.

First, there's extra work to decide if the URL needs encoded parameters. I made the parameters optional because not every GET request requires parameters. The next big change is fewer headers need to be configured: the method is set to GET, and Content-Encoding isn't needed.

The next 2 lines are how I think the JWT is added. It's very simple, just building a string and adding a header.

Everything past that is identical: the request is sent, the response is returned.

Now we have the ability to send an HTTP GET method with URL parameters and a JWT. That's what we needed to implement /search/series! I don't want to have to think about HTTP when calling that method, so I'm thinking it needs to look like this:
Code:
Public Function SearchSeriesByName(ByVal jwt As String, ByVal searchTerm As String) As SearchSeriesResult
Ah. Right. I forgot something. I need an object that represents the search result. That JSON is defined in the documentation. This object should do the trick:
Code:
Public Class SearchSeriesResult
    Public Property aliases() As String
    Public Property banner As String
    Public Property firstAired As String
    Public Property id As Long
    Public Property network As String
    Public Property overview As String
    Public Property seriesName As String
    Public Property status As String
End Class
Now I write the relevant code in the TvDbApi class:
Code:
Imports Newtonsoft.Json

Public Class TvDbApi

    Public Function Login(ByVal credentials As TvDbCredentials) As String
        ...
    End Function

    Public Function SearchSeriesByName(ByVal jwt As String, ByVal searchTerm As String) As SearchSeriesResult
        Dim restClient As New RestClient()
        Dim url As String = "https://example.com/search/series"
        Dim parameters As New Dictionary(Of String, String)()
        parameters.Add("name", searchTerm)

        Dim responseJson As String = restClient.GetWithJwt(url, jwt, parameters)
        Dim responseObject As SearchSeriesResult = JsonConvert.DeserializeObject(Of SearchSeriesResult)(responseJson)
        Return responseObject
    End Function

End Class
Calling this should be relatively easy from the context of the main program:
Code:
Dim credentials As New TvDbCredentials()
credentials.apikey = "apikey asdf"
credentials.username = "username asdf"
credentials.userkey = "userkey asdf"

Dim tvDbApi As New TvDbApi()
Dim jwt As String = tvDbApi.Login(credentials)
Dim searchResults As SearchSeriesResult = tvDbApi.SearchSeriesByName(jwt, "Twin Peaks")

Console.WriteLine("First aired: {0}", searchResults.firstAired)
No muss, no fuss. We already know how to get the JWT, so it just gets included with this next call. See how minor a detail the JWT actually is?

Now you're on your own to implement the rest of this API. You might notice I didn't write a way to include a JWT with Post(). That's because when I was implementing Login(), I didn't need a JWT because I didn't have one. But if you needed PostWithJwt(), you have the tools to do it now. It doesn't look like this API actually uses any other POST methods. It does use HEAD, GET, and PUT, though, so you'll need to implement those.

This is how I write web API clients. First, I study the documentation to see what I need to do to use a particular endpoint. Then, I ask if my current RestClient class can satisfy those capabilities. If not, I implement a new RestClient method that can. When RestClient has enough features, I look at my "API" type and decide how to put a friendly function around the HTTP. Usually that involves making a new JSON object for inputs and outputs. The API method never does anything complex, it passes the URL, JSON, and any parameters to the RestClient and gets JSON back. Then it deserializes the JSON to an object and returns something.

That way, my application worries about "What things am I doing" rather than "Oh wait to search for series I first need to configure a GET request..."

If this code doesn't work in some way, post as many details about what's going wrong as you can. I can try to remote debug it. I left out any real attempt at handling errors, so it'll faceplant if you do something the API doesn't like. Error handling complicates tutorials.