First Heroku App: Small API in PHP to Talk to .Net Core

I’ve recently been curious about switching to a time API for my time stamps and removing any dependency the app might have on the server for a timestamp.  Upon Googling I found some paid services, some free and of the free ones, I noticed one was hosted on Heroku.  I’ve heard of Heroku, but never had a reason to attempt to use it.  This was the perfect chance.

How I Created a Small “GetTime” API

First, I created a free account on Heroku, nothing special.  After verifying my email, I logged in to my Heroku Dashboard and up on the right hand corner, selected Create New App.  I named it my company-api and out popped an app.

I decided on just plain, legacy PHP and a simple DateTime string passed thru JSON encode, just to get started.  No authentication, no timezone,  just a simple spit out if a request to the site came, like this:

<?php
    header('Content-type:application/json;charset=utf-8');

    $DateTime = new DateTime();
    $currentTime = $DateTime->format("Y-m-d H:i:s");
    echo json_encode($currentTime);
?>

I created a Git repo for this brand new file and pushed it out.  Then, I went back to Heroku, Dashboard, My App and Deploy.  I selected Github as my deploy “resource” and selected the new repo I just made along with correpsonding branch.

I hit manual deploy and Heroku runs off to my GitHub repo, grabs the code, compiles and publishes.

It failed.

Beginner Troubles

My first problem was that Heroku could not determine what language I had chosen for my app (you’d think the <?php would give it away …).  You need either one of two things:  a composer.json file or an index.php file (for legacy, like mine).  I renamed my file to index.php and all I needed now was a “builder pack”.

To add a builder pack, I went back to Heroku, Dashboard, My App and Settings.  Under builder pack, I added one for: “php”.  Save settings and done.

I went back to Deploy, Manual Deploy and had a successful output.  Yay!  First Heroku app!

Adding Some Security

I want to make sure this API is receiving and sending JSON, so there’s a few IF’s I make the request go through before I hit logic on my PHP page.  I also want to (lightly) secure the requests made to this API and monitor our usage of it for metrics information (and future investment).  Since this itty bitty API is just relying on 1 index.php file, I figure this can be a sort of “router” for future API’s.  So, this is what I added to the final PHP file:

  • Verify the request is in JSON
  • Require two variables passed:
    • a secret api “key”
    • an api “request”
  • Use a switch statement to forward to needed method

 

The final, simple 1 page PHP Heroku API:

<?php
header('Content-type:application/json;charset=utf-8');

//Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
    throw new Exception('Request method must be POST!');
}

//Make sure that the content type of the POST request has been set to application/json
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
$contentIsJson = strpos($contentType, "application/json");

if ($contentIsJson === false){
    throw new Exception('Content type must be: application/json');
}

//Receive the RAW post data.
$content = trim(file_get_contents("php://input"));

//Attempt to decode the incoming RAW post data from JSON.
$decoded = json_decode($content, true);

$app = strtoupper($decoded['API']);
$key = $decoded['APIKEY'];

//verify user key - simple MD5 generator: http://onlinemd5.com/.  will build user management for keys if ever needed
if ( $key == "BEAF1CB722A3F7758C7A7FA43F6BF2D1" )
{   

    switch ($app) {
        case "TIME":
            $jsonString = getTime();
            $arr = array('datetime' => $jsonString);
            break;
        default:
            $arr = array('error' => "Unknown Request On API");
            break;
    }  

    echo json_encode($arr);
}


//return the current time
function getTime ()
{
    $DateTime = new DateTime();

    //by default heroku returns time in UTC - can change in dashboard, config vars, only use as needed below
    //$DateTime->modify('-6 hours');
    $currentTime = $DateTime->format("Y-m-d H:i:s");

    $jsonString = $currentTime;

    return $jsonString;
}

?>

 

Verifying the Response

I used Postman to send a raw JSON request to my Heroku app (used their default/free url).  I wanted to make sure all my problems were resolved with this new toy, first, and then move on.  Here’s what the raw request and response look like on Postman:

Heroku + my PHP are responding nicely!

Handling the Request and Response in C#

So here’s how I did the same request and received the response in C# (I use dotnet Core):

public async Task<string> OurHerokuAPI()
{
    string reqUrl = "https://mycompany-api.herokuapp.com";

    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders
            .Accept
            .Add(new MediaTypeWithQualityHeaderValue("application/json"));

        try
        {

            var query = new
            {
                APIKEY = "BEAF1CB722A3F7758C7A7FA43F6BF2D1",
                API = "time"
            };

            var asJson = JsonConvert.SerializeObject(query);
            HttpResponseMessage response = await client.PostAsync(reqUrl, new StringContent(asJson, Encoding.UTF8, "application/json"));

            if (response.IsSuccessStatusCode)
            {
                var definition = new { datetime = string.Empty };
                var json = JsonConvert.DeserializeAnonymousType(response.Content.ReadAsStringAsync().Result, definition);

                time = json.datetime;

                //monitors success across various "time" api's. in case this particular one fails, there can be various backups until success flag returns true.
                success = true;
            }
        }
        catch (OperationCanceledException)
        {
            //TODO: CREATE ERROR MESSAGE SEND BACK TO EMAIL/ERROR 
        }
    }
    return time;
}

 

If you see notes, yes, I decided to have some fun and create maybe two other functions, just like the one above, where I query some “free” time api’s until that “success” flag turns true.  After x tries, I reluctantly call my last function, that gets the server timestamp.  As said, I wanted my timestamp to be independent of server.  So, if it hits this last method, I also send myself an error email that the time API is failing.

In the future, I could  use environment variables (config vars) more wisely, instead of hardcoding.  There’s also so much clean up to do, but this was a very fun intro into Heroku!

Continue Reading

Share Azure Blobs with Search and Tree View

The idea behind this was to create a nice, easy UI that users can download media files they request often.  We moved it to Azure to prevent killing our on-prem bandwidth, but then I had to deal with the flat file structure, etc.  The end result was simply: a fast search of all the blobs (with link) and underneath that, a tree structure of the blobs that they can browse through.

The Set Up

First, I created a Storage container through Azure Portal, then I used Azure Storage Explorer to create a Blob Container under that storage account.  I also set read-only permissions to my blobs by right clicking the container in Azure Storage Explorer, then: Set Container Public Access Level > Public Read Access for Blobs Only.

To make this code work, I needed to setup an environment variable for my connection string.  I used the prefix CUSTOMCONNSTR_ on my variable name as it comes in handy when deploying to Azure Web Apps.  To get the connection string:  Azure Portal > Storage Account you created > Access Keys.

setx CUSTOMCONNSTR_storageConnectionString "<yourconnectionstring>"

Finally, I got a folder I wanted to share and dragged and dropped it into my Container using Azure Storage Explorer.

Tools Used

I used .NET Core to query the container and list the blobs, with segments.  I then looped through that list, creating a formatted JSON string that I could feed to zTree (I preferred the Font-Awesome styling shown here).   I also simultaneously created a list of the blobs into formatted JSON that I could also feed to Ajax for a quick search.

The Code

Check out the code on GitHub

Demo

Sharing Azure Blobs on Azure Web Services

 

When deploying to Azure Web App Services:  Web App > Application Settings > Connection Strings, name is: storageConnectionString, value:  string copied from container assets, type: Custom

Continue Reading

.Net Core – Drop Down (Select) Won’t Populate

I had a recent bug that took me much too long to solve and the root of the cause was me. I got a bit too delete happy and deleted a crucial file that allows me to use Razor Tag Helpers to bring a List into a View as a Drop Down (Select).

The Error

Here’s how the error presented itself:

  • SelectList won’t populate
  • Select or DropDown is empty
  • value = Microsoft.AspNetCore.Mvc.Rendering.SelectList or
  • System.Linq.OrderedEnumerable2 [Microsoft.AspNetCore.Mvc.Rendering.SelectListItem, System.String]
Continue Reading

Print PDF’s on Azure Using an API and RazorLight

The Problem

I recently had an issue with printing a report to PDF using Microsoft Reporting Service and a RDLC file, etc. Something similar to this. Unfortunately, it worked great in development, but refused to work once deployed into Azure. No matter what I did, I could not duck the GDI errors I kept getting, and apparently this continues through a line of various PDF exporting extensions, all of which rely on GDI for export. Turns out, I’m not alone in facing this problem and so, I decided to find a solution.

The Solution

My general idea was to use something to render my PDF view, send that view as one long html string to a free PDF microservice and get the PDF in return.

Continue Reading

Let’s Build

New Environment, New Approach

It’s Day 1, my first Microsoft Build, and I was not prepared for the sheer numbers nor format. I tediously worked out this schedule with sessions and back up sessions (in case, I don’t know… tripped on the way to the first one?). I lost my OCD mind when I realized these sessions were held open format in the MIDDLE OF THE EXPO!

I quickly realized the “sessions” were to show off their specialty. To draw you in by topic, touch on some “new” topics you hadn’t heard of, and get you to come talk to them. The presentation was not so much a sales pitch as it was a “let me help you develop on our platform.” Interesting…

Continue Reading

PayTrace: 400 Bad Request For Declined Payment

TLDR:  post IS successful.  PayTrace, by design, returns a 400 error, which sets off exceptions in httpresponse.  Solution: catch the exception and then continue deserializing your response.

I coded a few weeks ago a .NET post to the PayTrace API which helps me demo and test payment by credit card using client side encryption.  The process more or less went like this:

  • Create demo account as a merchant on Paytrace
  • Download PEM key
  • On submit of form with credit card information, an imported PayTraceJS library encrypts the card number and csc code
  • Use the demo account’s username and password to submit a request for a token
  • Submit transaction (which includes encrypted info as well as other required fields) using token and await response

A successful http response returns a status code of 200.  I read it via stream, deserialize it using json into my CardResponse object (both successful and failure responses have the same design).  Everything went great until I began testing rejected cards.

Continue Reading