Marvel API Background on UWP Application

Marvel API Background on UWP Application

“Genius, Billionaire, Playboy, Philanthropist” ~ Tony Stark”

Description

A C# Model, Mainpage.xaml.cs, and a Mainpage.xaml view in a Universal Windows Platform (UWP) application are used to showcase a random Marvel Character from the Marvel API. The first image appears (starting at 20 seconds) and it continues to display a random character every 20 seconds. Very simple, but fun.

Known Issues

If you want to use this in an application that you will actually use, you should filter out the images that are not available. I did filter some out already.

Model

You will need to go over to https://developer.marvel.com/ to get your own API keys to use in this model. To use this model, just input your keys in place of the publickey and privatekey.

The MD5 hash was the hardest part, but thanks to Channel 9, reference below, I was able to get it working.

A random offset between 0 and 1400 is used to randomize the returned results. Then from the page of results, a random number is picked. Yes, I am twice randomizing the results. Probably one too many times.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;

namespace GetMarvelCharacter.MarvelAPI
{
    class GetMarvelCharacter
    {
        private const string publickey = "enter key";
        private const string privatekey = "enter key";

        public async static Task<RootObject> GetCharacter(int CharacterID)
        {
            try
            {
                Random random = new Random();
                int offset = random.Next(0, 1400);
                string requesturl = "https://gateway.marvel.com:443/v1/public/characters?offset=" + offset + CreateHash();
                System.Diagnostics.Debug.WriteLine(requesturl);
                var http = new HttpClient();
                var response = await http.GetAsync(requesturl);
                var result = await response.Content.ReadAsStringAsync();
                var serializer = new DataContractJsonSerializer(typeof(RootObject));
                var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
                if (response.IsSuccessStatusCode == true)
                {
                   if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable() == true)
                    {
                        var data = (RootObject)serializer.ReadObject(ms);
                        return data;
                    }
                    else
                   {
                        return null;
                    }
                }
               else
               {
                   return null;
                }
               
            }
            catch 
            {
             return null;
            }
        }

        private static string CreateHash()
        {
            var timestamp = DateTime.Now.Ticks.ToString();
            var toBeHashed = timestamp + privatekey + publickey;
            var hashedmessage = ComputeMD5(toBeHashed);
            var urlsuffix = "&ts=" + timestamp + "&apikey=" + publickey + "&hash=" + hashedmessage;
            return urlsuffix;
        }
        private static string ComputeMD5(string str)
        {
            var alg = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
            IBuffer buff = CryptographicBuffer.ConvertStringToBinary(str, BinaryStringEncoding.Utf8);
            var hashed = alg.HashData(buff);
            var res = CryptographicBuffer.EncodeToHexString(hashed);
            return res;

        }
    }
    [DataContract]
    public class Thumbnail
    {
        [DataMember]
        public string path { get; set; }
        [DataMember]
        public string extension { get; set; }
    }
    [DataContract]
    public class Comics
    {
        [DataMember]
        public int available { get; set; }
        [DataMember]
        public string collectionURI { get; set; }
        [DataMember]
        public List<object> items { get; set; }
        [DataMember]
        public int returned { get; set; }
    }
    [DataContract]
    public class Series
    {
        [DataMember]
        public int available { get; set; }
        [DataMember]
        public string collectionURI { get; set; }
        [DataMember]
        public List<object> items { get; set; }
        [DataMember]
        public int returned { get; set; }
    }
    [DataContract]
    public class Stories
    {
        [DataMember]
        public int available { get; set; }
        [DataMember]
        public string collectionURI { get; set; }
        [DataMember]
        public List<object> items { get; set; }
        [DataMember]
        public int returned { get; set; }
    }
    [DataContract]
    public class Events
    {
        [DataMember]
        public int available { get; set; }
        [DataMember]
        public string collectionURI { get; set; }
        [DataMember]
        public List<object> items { get; set; }
        [DataMember]
        public int returned { get; set; }
    }
    [DataContract]
    public class Url
    {
        [DataMember]
        public string type { get; set; }
        [DataMember]
        public string url { get; set; }
    }
    [DataContract]
    public class Result
    {
        [DataMember]
        public int id { get; set; }
        [DataMember]
        public string name { get; set; }
        [DataMember]
        public string description { get; set; }
        //  [DataMember]
        //  public DateTime modified { get; set; }
        [DataMember]
        public Thumbnail thumbnail { get; set; }
        [DataMember]
        public string resourceURI { get; set; }
        [DataMember]
        public Comics comics { get; set; }
        [DataMember]
        public Series series { get; set; }
        [DataMember]
        public Stories stories { get; set; }
        [DataMember]
        public Events events { get; set; }
        [DataMember]
        public List<Url> urls { get; set; }
    }
    [DataContract]
    public class Data
    {
        [DataMember]
        public int offset { get; set; }
        [DataMember]
        public int limit { get; set; }
        [DataMember]
        public int total { get; set; }
        [DataMember]
        public int count { get; set; }
        [DataMember]
        public List<Result> results { get; set; }
    }
    [DataContract]
    public class RootObject
    {
        [DataMember]
        public int code { get; set; }
        [DataMember]
        public string status { get; set; }
        [DataMember]
        public string copyright { get; set; }
        [DataMember]
        public string attributionText { get; set; }
        [DataMember]
        public string attributionHTML { get; set; }
        [DataMember]
        public string etag { get; set; }
        [DataMember]
        public Data data { get; set; }
    }
}

Mainpage.Xaml.cs Class (UWP Application)

This class includes a 20 second rotation for a background image in a UWP application. I have included a random index selector to pick a random character from the returned results. If the image is not available, the code does not show the image, but shows a local image of wonderwoman. Note that the local image is found in the applications image directory. You will need to add your own local image and reference your local directory.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Serialization;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace GetMarvelCharacter
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            try
            {
                GetMarvelCharacter();
            }
            catch
            {
                LblError.Text = "Can't set marvel character.";
            }
        }
        private void GetMarvelCharacter()
        {

            var backgroundtimer = new DispatcherTimer
            {
                Interval = TimeSpan.FromSeconds(20)
            };
            backgroundtimer.Tick += async (o, e) =>
            {
                //Try to get Iron Man
                MarvelAPI.RootObject mycharacter = await MarvelAPI.GetMarvelCharacter.GetCharacter(1009610);
                //Make sure you received good data. Class returns null if something is broken.
                if (mycharacter != null)
                {
                    int resultindex = (mycharacter.data.results.Count) - 1;
                    Random random = new Random();
                    int randomindex = random.Next(0, resultindex);
                    string badimage = "image_not_available";
                    string imageurl = mycharacter.data.results[randomindex].thumbnail.path + "." + mycharacter.data.results[randomindex].thumbnail.extension;
                    //If the image is not available, change background to wonderwoman image. You need to add your own image, I am only providing a blank image.
                    if (imageurl.Contains(badimage))
                    {
                        imgBackground.Source = new BitmapImage(new Uri(this.BaseUri, "Assets/Backgrounds/wonderwoman.png"));
                        LblCharacter.Text = "Wonder Woman :)";
                    }
                    if (!imageurl.Contains(badimage))
                    {
                        imgBackground.Source = new BitmapImage(new Uri(imageurl, UriKind.Absolute));
                        LblCharacter.Text = mycharacter.data.results[randomindex].name;
                    }
                }
                else
                {
                    imgBackground.Source = new BitmapImage(new Uri(this.BaseUri, "Assets/Backgrounds/wonderwoman.png"));
                    LblCharacter.Text = "Wonder Woman :)";
                }
                };
                backgroundtimer.Start();
    }
    }

Mainpage.Xaml View

Page
    x:Class="GetMarvelCharacter.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GetMarvelCharacter"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock x:Name="LblError" TextWrapping="Wrap" TextAlignment="Center" Margin="447,125,0,0" Width="265" Height="78" FontSize="12" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        <Image Name="imgBackground" RequestedTheme="Light" Opacity="0.5" Height="480" Width="800" HorizontalAlignment="Center" VerticalAlignment="Center" />
        <TextBlock x:Name="LblCharacter" TextWrapping="Wrap" TextAlignment="Center" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>

    </Grid>
</Page>

References

Full Code on Github

Json2csharp Json to C# was used to generate the attributes for the Model Class. You can simulate your applications json results by using the API documentation on the Marvel API and getting back test results in json format. To make a Model for any other Marvel API call, simply copy and paste the json results into json2csharp, copy to a C# class, and build the model similar to how I have it above.

Visual Studio Community

Marvel API

Channel 9

Share this post: LinkedIn Reddit WhatsApp Mastodon
Jesse Borden

Jesse Borden

Software Engineer with an interest in hands on learning

I have several years of professional Information Technology (IT) experience leading staff and projects within the Department of War (DOW). I have managed Service Desk, Web Application Development, and System Administration teams. My two greatest passions are learning and conti...