Rest API via C#

Forum for users and developers of Bullhorn's new REST API service.

Moderators: StaffingSupport, s.emmons, BullhornSupport

Post Reply
sbrown
User
Posts: 1
Joined: Wed Nov 14, 2018 4:26 pm

Rest API via C#

Post by sbrown » Thu Nov 15, 2018 9:59 am

Does anyone have a sample using c# to get an authorization token and login to Rest API?

riyasathe
User
Posts: 2
Joined: Sat Mar 16, 2019 3:18 am

Re: Rest API via C#

Post by riyasathe » Sat Mar 16, 2019 3:27 am

Not yet! I have a same question.

newclique
User
Posts: 3
Joined: Fri Mar 22, 2019 6:21 pm

Re: Rest API via C#

Post by newclique » Fri Mar 22, 2019 6:26 pm

Here's how I do it in a console app. Be sure to add the necessary references and Newtonsoft nuget pkg:

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Web;

using System.IO;
using Newtonsoft.Json;

namespace GrabBullhorn
{
    class Program
    {
        static string _ClientID = "your-id-here";
        static string _Secret = "your-secret-here";
        static string _Username = "whatever.restapi";
        static string _Password = "your web login pwd";
        static string _authCode = "";
        static string _stateCode = Guid.NewGuid().ToString(); //prob not necessary
        static BullhornTokens _tokens = null;

        static void Main(string[] args)
        {
            _authCode = getBHAuthCode();
            if (!string.IsNullOrEmpty(_authCode))
            {
                _tokens = getAccessToken();
                if (_tokens != null)
                {
                    //can make api calls for 10 minutes...

                    //...or make a refresh token call, get a refresh token (if your account is allowed one), and then make calls for hours
                    Console.WriteLine("Authentated successfully! You can now implement your API calls for entities, etc.");
                    Console.WriteLine("Press {Enter} to exit...");
                    Console.ReadLine();
                    Environment.Exit(0);
                }
            }
            Console.WriteLine("Unable to authenticate and generate an access token.\nPress {Enter} to exit...");
            Console.ReadLine();
            Environment.Exit(-1); //booo!
        }

        static string getBHAuthCode()
        {
            string url = $@"https://auth.bullhornstaffing.com/oauth/authorize?client_id={_ClientID}&response_type=code&action=Login&username={_Username}&password={_Password}&state={_stateCode}";
            
            var request = HttpWebRequest.CreateHttp(url);
            request.Method = "GET";
            var responseObj = (HttpWebResponse)request.GetResponse();
            string response = null;

            using (var streamReader = new StreamReader(responseObj.GetResponseStream()))
            {
                if (responseObj.StatusCode == HttpStatusCode.OK)
                {
                    var vals = System.Web.HttpUtility.ParseQueryString(responseObj.ResponseUri.Query);
                    if (vals.HasKeys() && vals["code"] != null)
                    {
                        response = vals["code"];
                    }
                }
            }
            
            return response;
        }

        static BullhornTokens getAccessToken()
        {
            string url = $@"https://auth.bullhornstaffing.com/oauth/token?grant_type=authorization_code&code={_authCode}&client_id={_ClientID}&client_secret={_Secret}";
            var request = HttpWebRequest.CreateHttp(url);
            request.Method = "POST";
            request.Accept = "application/json";
            var responseObj = (HttpWebResponse)request.GetResponse();

            using (var streamReader = new StreamReader(responseObj.GetResponseStream()))
            {
                if (responseObj.StatusCode == HttpStatusCode.OK)
                {
                    var content = streamReader.ReadToEnd();
                    var tokens = JsonConvert.DeserializeObject<BullhornTokens>(content);
                    return tokens;
                }
            }
            return null;
        }

    }

    public class BullhornTokens
    {
        public BullhornTokens()
        {
            this.TokensCreated = DateTime.Now;
        }
        public DateTime? TokensCreated { get; private set; }

        [JsonProperty(PropertyName="access_token")]
        public string AccessToken { get; set; }
        [JsonProperty(PropertyName ="expires_in")]
        public int ExpiresInSeconds { get; set; }
        [JsonProperty(PropertyName ="token_type")]
        public string TokenType { get; set; }
        [JsonProperty(PropertyName="refresh_token")]
        public string RefreshToken { get; set; }

        DateTime? _expiration;
        public DateTime? GetExipration()
        {
            if (_expiration.HasValue || !TokensCreated.HasValue)
                return _expiration;

            _expiration = TokensCreated.Value.AddSeconds(ExpiresInSeconds);
            return _expiration;
        }
    }
}

newclique
User
Posts: 3
Joined: Fri Mar 22, 2019 6:21 pm

Re: Rest API via C#

Post by newclique » Mon Apr 08, 2019 3:59 pm

Oops, I left out two final steps. First, you have to get the REST Token (could this API be any more complex?) and the CorpID with yet another login call. Second, you have to use these new pieces of information to try to call the REST services themselves.

Code: Select all

_tokens = getAccessToken();
                if (_tokens != null)
                {
                    _RESTInfo = getBHRESTLoginInfo();
                    if (_RESTInfo != null)
                    {
                        //can make api calls for 10 minutes...
                        var url = new Uri(_RESTInfo.REST_URL);
                        string corptoken = url.Segments[url.Segments.Count()-1];
                        corptoken = corptoken.Remove(corptoken.Length - 1, 1);
                        string fieldlist = "id,title";
                        //can make api calls for 10 minutes...
                        var request = HttpWebRequest.CreateHttp($@"https://rest.bullhornstaffing.com/rest-services/{corptoken}/search/JobOrder?BhRestToken={_RESTInfo.REST_Token}&fields={fieldlist}");
                        
                        request.Method = "GET";
                        var responseObj = (HttpWebResponse)request.GetResponse();
                        string response = null;

                        using (var streamReader = new StreamReader(responseObj.GetResponseStream()))
                        {
                            if (responseObj.StatusCode == HttpStatusCode.OK)
                            {
                                var vals = System.Web.HttpUtility.ParseQueryString(responseObj.ResponseUri.Query);
                                if (vals.HasKeys() && vals["code"] != null)
                                {
                                    response = vals["code"];
                                }
                            }
                        }
                        //...or make a refresh token call, get a refresh token (if your account is allowed one), and then make calls for hours
                        Console.WriteLine("Authentated successfully! You can now implement your API calls for entities, etc.");
                        
I had to implement one more class to hold the de-serialized JSON of this rest login call:

Code: Select all

    public class BullhornRESTLoginInfo
    {
        [JsonProperty(PropertyName = "BhRestToken")]
        public string REST_Token { get; set; }
        [JsonProperty(PropertyName ="restUrl")]
        public string REST_URL { get; set; }
    }
Be warned: The examples in the API guide do not seem to be accurate. Currently, following those guides, I am getting this back from a call asking for all of my current JobOrder entities (using the exact format and path listed in their API guide):

Code: Select all

{"error":"This URL is reserved for login purposes."}
So, I dunno if it is me or it is their documentation. I'll post back when I actually get some data. Here's the URL format I am using (with my tokens, obviously):

Code: Select all

https://rest.bullhornstaffing.com/rest-services/{corptoken}/search/JobOrder?BhRestToken={_RESTInfo.REST_Token}&fields={fieldlist}

newclique
User
Posts: 3
Joined: Fri Mar 22, 2019 6:21 pm

Re: Rest API via C#

Post by newclique » Mon Apr 08, 2019 4:18 pm

Ugh. My last post had errors due to the API guide not being very clear about needing to use the returned REST_URL from the rest-login call to make all future calls. I'm sure this was obvious to some but the examples that show {corpToken} really threw me because I thought that meant I needed to use their exact example URLs and just tack on my token. IMHO, the API guide should {parameterize} everything in the URL that you need to sub out for yourself, e.g. {Returned REST_URL}/{your query here}

If this does't give someone enough information to finish the C# code, let me know and I will gladly post the finished product.

redaw77
User
Posts: 1
Joined: Thu May 16, 2019 4:18 am

Re: Rest API via C#

Post by redaw77 » Thu May 16, 2019 4:23 am

Hi, Thank you for posting this, its very helpful. I agree the BH documentation is a bit confusing, especially as it seem to constantly change.

I was just trying to use the code, but there is no method 'getBHRESTLoginInfo'. do you have this to share? Any help would be greatly appreciated.

Thanks

Post Reply