Click here to Skip to main content
13,707,159 members
Click here to Skip to main content
Add your own
alternative version

Stats

31.5K views
528 downloads
65 bookmarked
Posted 29 Apr 2015
Licenced CPOL

The CodeProject API - Part 1

, 29 Apr 2015
Rate this:
Please Sign up or sign in to vote.
In this article, we're going to look at how we can start building a client side CodeProject API that's suitable for all C# developments.

Series

Introduction

This is probably one of the most ambitious articles I will ever write for CodeProject. In this article, I want to cover how I have approached starting to build a wrapper for the CodeProject API. You may be asking yourself why you'll want to use yet another wrapper when you could interface directly with the API yourself but I hope that my aims convince you to read further.

A while back, I was speaking to Chris Maunder (well, pestering him for an API) and didn't he go and crack the whip until the team give us an API. Now, there have been a number of things I've been working on that have kept me away from playing with the API as much as I would like, but it kept niggling at me that I hadn't got around to working with the API – yet it was something I couldn't wait to try out. So, I got a little bit of free time set aside and decided that it was time to bite the bullet. I didn't want to produce just another wrapper when there are some excellent examples on here. Rather, I wanted to produce the wrapper that most closely resembled the way I approach development nowadays, and produce an article that details the way I approach this type of development. To that end, I decided that I needed to set down some objectives to guide me, which would serve as the basis of my "acceptance criteria". By the way, if you don't have acceptance criteria for your projects, you should start putting them in place – how can you know you've successfully achieved what you set out to do if you don't have something written down to mark that against.

So, here are my objectives for this iteration.

Objective 1

The wrapper has to be Portable Class Library compliant. As long as I adhere to strict PCL, I should be able to produce a wrapper that can be used in lots of environments. The first cut of the wrapper targets .NET 4.5, Windows 8, Windows Phone 8 and Windows Phone Silverlight 8. I will later be targeting Xamarin for this wrapper, so retargeting will take place that make it easy for us to use on Android and iOS as well.

Objective 2

The API has to follow what I consider to be good design principles as much as possible. Now, this is a highly subjective thing but, where possible, I will be aiming to use SOLID principles extensively. While the development will largely follow TDD, there will be times that I stray away from this in order to use some feature that is not easily testable, such as the HttpClient. If I follow Single Responsibility as much as possible, I will be minimising the "touchpoints" so to speak. Ultimately, I am going to be producing something for others to use so I have to be as pragmatic as possible – this means that there will be some design compromises but, I will note them down here.

Objective 3

This will be an iterative build. I am limiting the features that I produce for the first version to simply getting an access token - the token will be vital later on when we're using the API. This may seem to be a trivial thing but as there are a large number of design decisions that are going to go into the initial design, this is a good basis for getting something "out of the door" as a proving ground as a first pass. I would caution against relying too heavily on this version because it's highly likely that I will be refactoring things quite heavily in future builds but this is a good starting point.

Objective 4

I want to use IoC, but I don't want to force you to have to use my chosen IoC. Okay, that's a strange statement but as I would like to get this adopted by as many people as possible, I am designing it so that you can use whatever mechanism suits you. Ultimately, if you want to "new" everything up yourself, that's entirely your choice but I like IoC. Saying that, I don't expect that you would have to register everything yourself if you want to go IoC. The code provides an abstraction that we can use to hide the fact that we are using IoC, letting us drop in the IoC container we prefer. I'll be providing an Autofac implementation, and this will demonstrate how we can wrap things up in an appropriate IoC container.

Objective 5

Unless I have a very good reason, the code is going to follow the Single Responsibility Principle as much as possible. This means that there are going to be lots of small classes that hook things together. I'm a big fan of SRP.

Objective 6

I'm not going to write things I don't need to write. This means that I'm going to be using packages that other people have written for things like converting JSON unless I find that I need something that doesn't exists in PCL form.

Objective 7

Apart from sample projects and tests, all the libraries will be Portable Class Libraries. This just goes back to the fact I want this to be available as widely as possible.

Objective 8

Inputs aren't to be trusted. If a method or constructor accepts a type, at the very least, we should test to make sure it's set. There will be some validity checking introduced but the first stage is to make sure that every public constructor or method doesn't trust its inputs. This is easily verifiable through tests.

Objective 9

Initially, I will only be providing unit-level tests. I'm not going to be performing System-Under-Test (SUT) tests so inputs will be mocked as much as possible.

Objective 10

At this stage, I'm not worried too much about exception handling or logging. The code, for this iteration, is going to naively assume that everything is okay when it calls out to external sources. In the next iteration, I'll be looking to put more formal handling in to cater for the so-called "unhappy day" cases. This means that there will be no custom exceptions being raised at this stage – these will be introduced in later stages.

Okay, those should be enough objectives to convince you that I have a definite end-goal in mind, so let's see what we're going to need to allow us to build this version.

Prerequisites

Visual Studio 2013 Update 4

Telerik JustMock Lite. This is an open source mocking framework that I have a definite fondness for. For my company, I use the full version of JustMock because it does things like allow me to fully mock things like HttpClient so I can arrange on none-virtual methods as well as virtual ones. This feature isn't available in the open source version but it still does an awful lot – and it works with PCL.

Autofac. This is an extremely straightforward but powerful IoC container. I cannot recommend it highly enough.

Json.NET. At the time of writing, this was the only JSON converter I could find that worked as a PCL library so I chose this implementation.

Microsoft HTTP Client Libraries. By default, HttpClient is not provided as a PCL item. If we want to use it, we will end up downloading the PCL implementation.

Note that I used NuGet to get the last three prerequisites.

A Word About My Writing Style

Readers of my previous articles will probably be aware that I like to use a more inclusive "we" where I'm demonstrating how to write a piece of code. Over the years, I've found that this helps to make for a warmer article. As this article is going to deal heavily with the process I was following, and the decisions I was taking, unfortunately it's not going to use this format as much. Don't despair though as we will be seeing how to write our own code to integrate this library later on in the article and, as that will be more interactive, I'm hoping you'll join me in writing the code at that point.

Glossary of Terms

  • IoC – Inversion of Control.
  • SOLID – 5 key principles used in Object-Oriented design. Stands for Single responsibility, Open-Closed, Liskov substitution, Interface segregation, Dependency inversion.
  • YAGNI – You Aren't Gonna Need It. The idea that you don't write code just because you might need it at some point in the future.

The Wrapper API

Right from the start, I decided that I was going to break the API into two separate DLLs. The first DLL consists purely of interfaces. Effectively, this DLL lays out the contract that I'm going to follow with this development. This is a purely personal choice but is based on the fact that I frequently write systems where components are distributed around and I use MEF to compose things together automatically. By having a central interfaces library, the process effectively becomes a no-brainer for me.

CodeProject.Api.Http

The Http namespace is the one that I'm placing the functionality that is directly related to actually interacting with Http.

HttpClientBuilder

This class is, cunningly enough, responsible for building up the HttpClient.

public HttpClientBuilder(HttpClient client, IClientSettings clientSettings)
{
  if (client == null) throw new ArgumentNullException("client");
  if (clientSettings == null) throw new ArgumentNullException("clientSettings"); 

  client.BaseAddress = new Uri(clientSettings.BaseUrl);
  client.DefaultRequestHeaders.Accept.Clear();
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  this.client = client;
}

If we look at the definition of the constructor, we see that we are expected to pass in the HttpClient into this class. On seeing this, our first reaction is probably that this looks wrong and that if we are creating the HttpClient externally, we should be populating it there. The important thing to remember here is that I am favouring IoC, so we aren't going to creating the HttpClient ourselves – we are merely going to register HttpClient and let the IoC container take care of giving us an instance. This means, of course, that we have to have some mechanism of defining what we want setting up HttpClient – this, of course, means that I have created this small class to set it up for us.

The key things that we are setting up in this instance of HttpClient is the BaseAddress and the DefaultRequestHeaders, which we are setting to accept JSON. As we can see, our classes don't have to be terribly complicated when we build small self-contained units. What is interesting is, even though I know that I'm going to need to use different addresses in the future, I am keeping it as simple as possible right now. I'm not going to try and second-guess future implementations here and I'm not going to build architecture for code that I don't need in this iteration. While YAGNI isn't strictly a SOLID concept, it's an important one that has all sorts of implications about the way we write code – it's all too easy to write code that goes above and beyond what we need, just because we think that it might be handy at some point in the future.

ClientRequest

This is the class that is going to be responsible for actually issuing the POST request to the server, and converting the response to JSON. It's not going to do any translation of the JSON object, that responsibility lies elsewhere.

There's one method exposed by this class; PostRequestAsync. I am following the convention of having async tasks append Async to the operation name, this is a great way to let people know that your code is suitable for use with async/await.

public async Task<object> PostRequestAsync(FormUrlEncodedContent request)
{
  if (request == null) throw new ArgumentNullException("request");
 
  HttpClient client = clientBuilder.GetClient();
  HttpResponseMessage message = await client.PostAsync("Token", request);
  string jsonData = await message.Content.ReadAsStringAsync();

  var output = JsonConvert.DeserializeObject(jsonData);

  return output;
}

The first thing I do in here is ensure that we are passing in a request object. You will note that I'm not stating it's a valid request object, just that it's a request object. This is in keeping with my initial objectives that I don't trust inputs, even if it's me that's written the code calling this method. As the development progresses, inputs like this will be surrounded by more rigorous validation – I will be demonstrating some of this checking when we look at ClientSettings and UserDetails.

The next line shows us retrieving the HttpClient from HttpClientBuilder. IHttpClientBuilder has been injected into this class just to provide this capability.

Now, we want to send an asynchronous post request to retrieve our token. This is where we are starting to step outside of SRP – is this method doing too much? The answer to that, of course, is that it is doing far too much but I have decided to leave it as it is for the moment. I know that, at some point soon, I'm going to want to split this out because there are going to be more than access token tests but, right now, this is sufficient for my needs. Refactoring will occur at some point, it just doesn't have to be right now.

Finally, we want to read the response and deserialize it to JSON. This deserialization is the reason I haven't started to split this class up. Right now, I have elected to just retrieve a single property (this is done elsewhere), rather than hydrating the full model. Once I start to put richer models in place, the easier it will be to see how what shape I want this particular class to be.

Incidentally, testing of this particular method is less than the rigour I would like it to be. I have made a conscious decision to limit the set of tests here because I know that I want to mock out the HttpClient requests, and I have not put the full infrastructure in place for this particular implementation. I'm holding off on that one because I want to demonstrate, in the next article, how making a change is isolated when we use SRP.

CodeProject.Api.Json

JsonConverter

At the moment, this namespace consists of a single class. This class provides the ability to get the value out of a single token.

CodeProject.Api.Token

AuthenticationTokenPostData

The eagle eyed reader (well, the ones who've browsed the code already) will have noticed that there is a seeming mismatch between the number of interfaces declared in CodeProject.Api.Interfaces.Http and the number of classes in CodeProject.Api.Http. There's one more interface than there is class in the relevant namespaces; the "missing class" is implemented here because it's the one that I'm going to use to build the post data for the access token.

public FormUrlEncodedContent Build()
{
  clientSettings = validateClientSettings.Validate(clientSettings);
  userDetails = validateUserDetails.Validate(userDetails);

  List<KeyValuePair<string, string>> postData = new List<KeyValuePair<string, string>>();
  Add(postData, "grant_type", "password");
  Add(postData, "client_id", clientSettings.ClientId);
  Add(postData, "client_secret", clientSettings.Secret);
  Add(postData, "username", userDetails.Email);
  Add(postData, "password", userDetails.Password);

  return new FormUrlEncodedContent(postData);
}

When we call Build, we expect to receive an encoded access token list of items. What's most interesting here is that I've started to introduce some other input validation to ensure that we have the values that we need to actually issue the request. You might wonder why I'm validating at this point, rather than in the constructor – after all, I've talked about not trusting input but there doesn't appear to be any input parameters here. Simply put, input relates to any state that a method relies on and we validate here because this is the point we actually need to check the state. If we were to validate in the constructor, we would need to have populated the user details and client settings before. While that would be okay for client settings (we'll see why when we look at that class), we expect the user details to be more dynamic. Typically, we might want the user to enter the details at run time, long after the object graph has been constructed.

AccessToken

This class is the model that we're going to use to hold the access token. It's not going to be responsible for retrieving the token, that's the responsibility of FetchAccessToken, but it will be the entry point for our code to retrieve the token from the web server, and store it for easy access wherever it is needed.

FetchAccessToken

This class uses the AuthenticationTokenPostData class to build the encoded content and then uses ClientRequest to retrieve the access token, which is then decoded using the JsonConverter.

public async Task<string> GetTokenAsync()
{
  FormUrlEncodedContent encodedContent = encodedPostData.Build();
  var ret = await clientRequest.PostRequestAsync(encodedContent) as JObject;
  return jsonConverter.GetValueFromToken<string>(ret, "access_token");
}

CodeProject.Api.Settings

ClientSettings

This class provides the client id, secret and base url that we'll use to access the API online. These values are set up for us when we register an application with CodeProject. In the sample code, I have provided values for us to use against one of my registrations. I would urge you to create your own application, rather than assuming that I'm going to leave this one available permanently.

public class ClientSettings : IClientSettings
{
  public string ClientId
  {
    get { return "F_FV6JInrKlX35cgj8o_gmzKb17mArv-"; }
  }

  public string Secret
  {
    get { return "J8SRZ10DEWy7uCaWdYNnh5VhUV1ObOTWM73DCxTb9JP9Pa_qbfg_8A4YgtD0etst"; }
  }

  public string BaseUrl
  {
    get { return "<a href="https://api.codeproject.com/">https://api.codeproject.com/</a>"; }
  }
}

UserDetails

This class is the user details model that provides the email address and password for the user when they want to access the site to retrieve details about their account. While my every coding instinct tells me that I should be implementing INotifyPropertyChanged to raise property change notifications, I don't actually have a need to do that just yet, so that doesn't get added.

public class UserDetails : IUserDetails
{
  public string Email { get; set; }
  public string Password { get; set; }
}

CodeProject.Api

ModuleRegistration

The final class I want to talk about in the CodeProject.Api assembly implements an interface that isn't defined in the core CodeProject.Api.Interfaces assembly. ModuleRegistration implements the IModuleRegistration interface which is actually defined in CodeProject.Api.Ioc.Builder.

The reason this class is implemented here is because it is responsible for registering all of the relevant interface to type mappings so that client applications don't have to. What I have aimed to do here is have the registration completely agnostic of a particular IoC technology. Initially, all of the registrations are marked to register as single instances because that is all I need for this particular iteration, but this will be changed so that things like the validation are multiple instance.

public class ModuleRegistration : IModuleRegistration
{
  public void Register<T>(IRegistration<T> registration) where T : class
  {
    if (registration == null) throw new ArgumentNullException("registration");
    registration.RegisterAsSingleInstance<IFetchAccessToken, FetchAccessToken>();
    registration.RegisterAsSingleInstance<IAccessToken, AccessToken>();
    registration.RegisterAsSingleInstance<IUserDetails, UserDetails>();
    registration.RegisterAsSingleInstance<IValidate<IClientSettings>, ValidateClientSettings>();
    registration.RegisterAsSingleInstance<IValidate<IUserDetails>, ValidateUserDetails>();
    registration.RegisterAsSingleInstance<IEncodedPostData, AuthenticationTokenPostData>();
    registration.RegisterAsSingleInstance<IClientSettings, ClientSettings>();
    registration.RegisterTypeAsSingleInstance<HttpClient>();
    registration.RegisterAsSingleInstance<IClientRequest, ClientRequest>();
    registration.RegisterAsSingleInstance<IHttpClientBuilder, HttpClientBuilder>();
    registration.RegisterAsSingleInstance<IJsonConverter, JsonConverter>();
  }
}

Again, while it's tempting to build in support for features such as applying names to the registrations for type resolution, as I don't need this feature yet, I'm not going to build it in.

Okay, so that's covered the core API. Let's take a look and see how we can add our own IoC implementations and how we bootstrap things into place.

CodeProject.Api.Ioc.Builder.Autofac

Registration

This class provides the mapping between the high level IoC abstractions, and the actual underlying IoC calls in Autofac. The actual calls are pretty trivial, but they rely on us passing in the ContainerBuilder that we will be registering the dependencies against. Indeed, as we develop other IoC containers, we will see that they will have their own containers passed in, such as Unity's UnityContainer.

The underlying interface is a generic interface to allow us to specify the type of the container.

public class Registration : IRegistration<ContainerBuilder>
{
  public void RegisterAsSingleInstance<TInterface, TType>()
    where TType : class, TInterface
  {
    if (Container == null) throw new ArgumentNullException();

    Container.RegisterType<TType>().As<TInterface>().SingleInstance();
  }

  public void RegisterTypeAsSingleInstance<TType>() where TType : class
  {
    if (Container == null) throw new ArgumentNullException();

    Container.RegisterType<TType>().SingleInstance();
  }

  public ContainerBuilder Container { get; set; }
}

As we can see here, this code isn't terribly complex – but it does exactly what we need it to do. As I mentioned before, YAGNI means that I haven't added multiple instance support yet and while I would like to offer a fluent interface, I don't have a need to do this yet, so I will avoid doing so for this iteration.

At this point, it's worth noting that I do have an intent to add support for other IoC containers at a later iteration.

Bootstrapper

Okay, so we have the ability to register interfaces and types against the IoC container, but we need something to tie the module registration together with the creation of the IoC container. This is where the Bootstrapper comes in. This abstract class provides the entry point for applications that want to use the API. We will derive from this class when we want to hook the API up behind the scenes for us; we could do it all manually if we want, but I have provided this functionality so that we don't have to.

public abstract class Bootstrapper
{
  protected Bootstrapper()
  {
    Bootstrap();
  } 

  protected virtual void OnStartup()
  { 
  }

  protected virtual void RegisterAdditionalModules(IRegistration<ContainerBuilder> registration)
  {
  }

  protected IContainer Container { get; private set; }

  protected virtual void InitializeModules()
  {
    OnStartup();
 
    var containerBuilder = new ContainerBuilder();

    IModuleRegistration moduleRegistration = new ModuleRegistration();
    IRegistration<ContainerBuilder> registration = new Registration()
    {
      Container = containerBuilder
    };
    moduleRegistration.Register(registration);

    RegisterAdditionalModules(registration);

    Container = containerBuilder.Build();
  }

  private void Bootstrap()
  {
    InitializeModules();
  }
}

The heart of the bootstrapping process is InitializeModules. In this method, we create the ContainerBuilder that Autofac requires, and allocate it to the Registration class. We then call register in ModuleRegistration which takes care of registering the different parts of the API for us. The final part of this puzzle requires the code to build up the IoC registrations in the container.

I have provided OnStartup to allow our applications to do work before we begin initializing modules, and RegisterAdditionalModules to allow us to build up registration mappings in our applications. Effectively, this makes the Bootstrapper the "one stop shop" where all of our application registrations will reside. This is the pattern I will follow when I provide bootstrappers for additional containers.

It's worth noting here, that the tests behind the Bootstrapper are the ones that I am the least happy with. They are present but all they do is verify that the different methods are called. I have plans to extend these tests in the next iteration.

Our First Access Token App

I appreciate that we have covered a lot of ground here. It's time for us to start building an application. The simplest type of application we can build is, of course, a console application so we will start with that. I've called my particular application, CodeProject.Api.Autofac.ConsoleApplication but feel free to change that if that feels too clumsy for you.

Before we actually start adding functionality, we're going to need the following references:

References

Let's start by creating the interface that describes what we want to do. As we're going to retrieve an authorization token, we need a mechanism to get the users email address and password, and we need a method that will call GetTokenAsync from our AccessToken implementation; as we know that we are going to be calling an async method, let's make sure that our interface also exposes this method as a suitable candidate for an async method:

using System.Threading.Tasks;
namespace CodeProject.Api.Autofac.ConsoleApplication
{
  public interface IAuthTokenSample
  {
    /// <summary>
    /// Enter the email address and password.
    /// </summary>
    void EnterCredentials(); 

    /// <summary>
    /// Uses the API to retrieve the token.
    /// </summary>
    /// <returns>An awaitable task</returns>
    Task GetTokenAsync();
  }
}

So, that's the interface created. Let's move on and create an implementation of this class. Cunningly enough, I'm going to call the class AuthTokenSample.

using System;
using System.Threading.Tasks;
using CodeProject.Api.Interfaces.Settings;
using CodeProject.Api.Interfaces.Token;

namespace CodeProject.Api.Autofac.ConsoleApplication
{
  public class AuthTokenSample : IAuthTokenSample
  {
    public void EnterCredentials()
    {
    }

    public async Task GetTokenAsync()
    {
    }
  }
}

Now, this class depends on the access token which we cannot get until we have populated the user details, so that tells us that we are going to have to get a reference to IAccessToken and IUserDetails. We're going to use constructor injection here, to inject these references into the class, and store them for use later on.

private readonly IAccessToken accessToken;
private readonly IUserDetails userDetails;

public AuthTokenSample(IAccessToken accessToken, IUserDetails userDetails)
{
  if (accessToken == null) throw new ArgumentNullException("accessToken");
  if (userDetails == null) throw new ArgumentNullException("userDetails");

  this.accessToken = accessToken;
  this.userDetails = userDetails;
}

We have everything in place now, to start implementing the interface methods. Let's start by having the user enter their details.

public void EnterCredentials()
{
  do
  {
    Console.Write("Enter email address: ");
    userDetails.Email = Console.ReadLine();
  } while (string.IsNullOrWhiteSpace(userDetails.Email));

  do
  {
    Console.Write("Enter password: ");

    userDetails.Password = Console.ReadLine();
  } while (string.IsNullOrWhiteSpace(userDetails.Password));
}

The last thing we need to do, is retrieve and display the token.

public async Task GetTokenAsync()
{
  Console.WriteLine(await accessToken.GetTokenAsync());
}

It's that simple  - because the user details are stored in a single instance, the access token has full access to the populated email address and password. The amount of code we need to write at this side of the API is negligible.

The last stage of the puzzle is writing our Bootstrapper. As this is the start of our API journey, I have chosen to call this class, StartApi. Again, please choose whatever name you feel happiest with.

As we have added an interface and class that we want to be treated as part of the IoC lifecycle, our bootstrapper needs to register them before we can use them. Fortunately, the boostrapper lifecycle gives us RegisterAdditionalModules to include them in the registration build up.

Finally, we are going to provide a means to actually use our test implementation. As we're leveraging Autofac, our class is ultimately going to end up looking like this:

using Autofac;
using CodeProject.Api.Ioc.Builder;

namespace CodeProject.Api.Autofac.ConsoleApplication
{
  public class StartApi : Bootstrapper
  {
    protected override void RegisterAdditionalModules(IRegistration<ContainerBuilder> registration)
    {
      registration.RegisterAsSingleInstance<IAuthTokenSample, AuthTokenSample>();
      base.RegisterAdditionalModules(registration);
    }

    public async void TestAuthTokenAsync()
    {
      using (var scope = Container.BeginLifetimeScope())
      {
        var item = scope.Resolve<IAuthTokenSample>();
        item.EnterCredentials();
        await item.GetTokenAsync();
      }
    }
  }
}

Finally, add code to the Main and we're good to go – we can retrieve our access token.

using System;

namespace CodeProject.Api.Autofac.ConsoleApplication
{
  class Program
  {
    static void Main(string[] args)
    {
      StartApi api = new StartApi();
      api.TestAuthTokenAsync();
      Console.ReadKey();
    }
  }
}

And that's it. That's all we need to do to create our application. It really is that easy. Here's a view of our application in action:

The application in action.

Conclusion

At this stage, we have a lot of the basics in place for our API. There's a lot that we need to do, and we're going to have some refactoring to do as we add in additional functionality, but we have reached a good first step right now.

My next steps are to extend what the API retrieves, introduce features such as multi-instance IoC support, start looking at adding sensible exception handling, and catering for the unavailability of online resources, as well as beefing up tests and adding additional samples. What we have here is a good start, and I hope that the way I approach developments are clear here.

One final point. I talked about my objectives up front. At this stage, I can say that I have met the objectives I set out to achieve. Having objectives available up front makes things much easier to say whether or not we have achieved what we want to achieve.

Music

In my articles on developing the Ultimate Coder application, I set out the music I listened to while I was coding. Music helps me relax when the development is flowing, and this development is no exception; so the music I have listened to is:

  • Joe Satriani – The Complete Albums
  • Joe Bonamassa – Live From the Royal Albert Hall
  • Brad Gillis – Alligator
  • Pink Floyd – The Wall
  • Rush – Moving Pictures
  • Sixx A.M. – Modern Vintage
  • The Answer – Everyday Demons
  • Thunder – Wonder Days

Thanks for reading, and I hope you have enjoyed this article. If nothing else, you have an Autofac enabled way to get a CodeProject Access Token.

History

  • 2015-04-28 - Initial version submitted
  • 2015-05-07 - Linked to second article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pete O'Hanlon
CEO
United Kingdom United Kingdom
A developer for over 30 years, I've been lucky enough to write articles and applications for Code Project as well as the Intel Ultimate Coder - Going Perceptual challenge. I live in the North East of England with 2 wonderful daughters and a wonderful wife.

I am not the Stig, but I do wish I had Lotus Tuned Suspension.

You may also be interested in...

Comments and Discussions

 
QuestionIs there any way that I can get list of users using codeprojects api? Pin
13-Feb-18 21:59
member13-Feb-18 21:59 
QuestionHow to call codeproject api's in java Pin
13-Feb-18 21:56
member13-Feb-18 21:56 
GeneralMy vote of 5 Pin
Member 1288850922-Aug-17 16:52
memberMember 1288850922-Aug-17 16:52 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon22-Aug-17 20:50
protectorPete O'Hanlon22-Aug-17 20:50 
GeneralMy vote of 5 Pin
csharpbd28-Jul-16 5:58
professionalcsharpbd28-Jul-16 5:58 
GeneralMy vote of 5 Pin
BillWoodruff23-May-15 0:59
mvpBillWoodruff23-May-15 0:59 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon23-May-15 7:27
protectorPete O'Hanlon23-May-15 7:27 
GeneralMy vote of 5 Pin
AJSON10-May-15 6:26
mentorAJSON10-May-15 6:26 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon10-May-15 20:30
protectorPete O'Hanlon10-May-15 20:30 
QuestionMy Vote and A Question Pin
Agent__0077-May-15 17:31
professionalAgent__0077-May-15 17:31 
AnswerRe: My Vote and A Question Pin
Pete O'Hanlon7-May-15 22:27
protectorPete O'Hanlon7-May-15 22:27 
GeneralRe: My Vote and A Question Pin
Agent__0077-May-15 22:31
professionalAgent__0077-May-15 22:31 
QuestionGreat start Pin
Sacha Barber5-May-15 0:41
mvpSacha Barber5-May-15 0:41 
AnswerRe: Great start Pin
Pete O'Hanlon5-May-15 1:01
protectorPete O'Hanlon5-May-15 1:01 
AnswerRe: Great start Pin
Pete O'Hanlon6-May-15 20:50
protectorPete O'Hanlon6-May-15 20:50 
GeneralMy vote of 5 Pin
linuxjr1-May-15 7:10
professionallinuxjr1-May-15 7:10 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon1-May-15 21:04
protectorPete O'Hanlon1-May-15 21:04 
GeneralRe: My vote of 5 Pin
Pete O'Hanlon6-May-15 20:50
protectorPete O'Hanlon6-May-15 20:50 
GeneralRe: My vote of 5 Pin
linuxjr7-May-15 2:41
professionallinuxjr7-May-15 2:41 
QuestionThis is going to be good.... Pin
Stuart_King30-Apr-15 23:13
memberStuart_King30-Apr-15 23:13 
AnswerRe: This is going to be good.... Pin
Pete O'Hanlon30-Apr-15 23:27
protectorPete O'Hanlon30-Apr-15 23:27 
GeneralRe: This is going to be good.... Pin
Stuart_King30-Apr-15 23:58
memberStuart_King30-Apr-15 23:58 
GeneralRe: This is going to be good.... Pin
Pete O'Hanlon1-May-15 0:06
protectorPete O'Hanlon1-May-15 0:06 
AnswerRe: This is going to be good.... Pin
Pete O'Hanlon6-May-15 20:51
protectorPete O'Hanlon6-May-15 20:51 
QuestionLooks great Pin
Brady Kelly30-Apr-15 20:12
memberBrady Kelly30-Apr-15 20:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180920.1 | Last Updated 29 Apr 2015
Article Copyright 2015 by Pete O'Hanlon
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid