How to Build a Book Barcode Scanner in Blazor C#

Microsoft released the latest update to their development platform late last year, named .NET 5. As new platforms and updates arrive, developers face a struggle between shifting to the latest technologies or sticking with old faithful. With considerations such as support for legacy systems, stability, uptime and simply how best to spend your time, it poses quite the challenge to keep up to date with the development world. 

Because of all this, I hadn’t had a chance to look into .NET 5 until recently where I completed a course on Pluralsight during the weekend. This got me in the mood to create a practice application to test drive the improvements that have been made to the platform.

Funnily enough, I didn’t end up directly using any of the new features but instead built something that I thought was quite cool and that I wanted to share. I wanted to build something that I could potentially expand on in the future and that is also related to my interests. I’ve long thought of how I can use software to better help myself remember what I read or improve my reading experience/efficiency. How this will be achieved still evades me, however I built one component that could be of use.

In this article I will show you how to build an application that can scan the barcode of a book (or any barcode for that matter, it’s just a case of configuring it for the correct type of barcode) and obtain the ISBN of the book. We will then call Google’s Books API in order to retrieve the information of the book that has been scanned. The application will be built in Blazor and we will be using QuaggaJS in order to achieve the barcode reader.

Firstly, we will create a component that will return us the ISBN of the book being scanned. Start by downloading QuaggaJS from https://serratus.github.io/quaggaJS/ and placing it inside your project; I have created a lib folder inside wwwroot and placed the download there.

In wwwroot, create a js folder and inside, create a JavaScript file named barcodeScanner.js. Place the following code inside:

function InitBarcodeScanner(dotnetHelper) {

    Quagga.init({
        inputStream: {
            name: "Live",
            type: "LiveStream",
            target: document.querySelector('#barcodeScannerElement') 
        },
        decoder: {
            // This is the type of barcode we are scanning. 
            // For barcodes other than books/ ISBNs, change this value.
            readers: ["ean_reader"]
        }
    }, function (err) {
        if (err) {
            console.log(err);
            return
        }
        console.log("Initialization finished. Ready to start");
        Quagga.start();
    });

    // When a barcode is detected...
    Quagga.onDetected(function (result) { 

        // Obtain ISBN.
        var code = result.codeResult.code; 

        // Pass to a .NET method.
        dotnetHelper.invokeMethodAsync("OnDetected", code); 

        Quagga.stop();
    });

}

Go to your _Host.cshtml file and add a reference for dist/quagga.min.js and barcodeScanner.js. The bottom of our _Host.cshtml should look like so:

In the project root, create a folder for Components and create a new Razor Component named BarcodeScanner. Place the following code inside:

<button class="btn btn-primary" @onclick="InitializeBarcodeScanner">Scan</button>

<div id="barcodeScannerElement" style="@barcodeScannerElementStyle"></div>

@code {
    [Inject] IJSRuntime JS { get; set; }

    [Parameter] public EventCallback<string> OnISBNDetected { get; set; }

    string barcodeScannerElementStyle;

    private async Task InitializeBarcodeScanner()
    {
        barcodeScannerElementStyle = string.Empty;
        var dotNetObjectReference = DotNetObjectReference.Create(this);
        await JS.InvokeVoidAsync("InitBarcodeScanner", dotNetObjectReference);
    }

    [JSInvokable]
    public async Task OnDetected(string isbn)
    {
        barcodeScannerElementStyle = "display:none;";
        await OnISBNDetected.InvokeAsync(isbn);
    }
}

Note that we have the HTML element of the same ID that was specified in the JavaScript file. InitializeBarcodeScanner will get called on press of the button. We create an instance of the component and pass this through to the JavaScript method, this is so that we can call the .NET method named OnDetected from JavaScript. When an ISBN is found, we will pass it back to the EventCallback that we will pass into the component later.

Add the BarcodeScanner component to the home page and create an empty EventCallback so that we can capture the ISBN:

@page "/"

<h1>Welcome to BookMate!</h1>

<BarcodeScanner OnISBNDetected="GetBookInformation" />

@code {
    private async Task GetBookInformation(string isbn)
    {
        
    }
}

By passing an event callback into the method, we can keep separate the code where we actually do something with the ISBN, as opposed to having this inside the component too. Keeping our concerns separate in this fashion helps to improve the reusability of the components that we create.

Now we can run the project and see the results. If you have a laptop with a webcam, this can be used to scan for a barcode. Let’s see what happens:

Quagga detects the ISBN…

…we then invoke OnDetected, passing the ISBN through to it…

…invoking the event callback.

Now that we are able to obtain an ISBN, we will now create a service that will fetch details on the book. Let’s do a test call to see what the data looks like. In a browser, put https://www.googleapis.com/books/v1/volumes?q=isbn: into the address bar, adding the ISBN of a book onto the end after the colon:

We can manually turn this into a class in which we can serialize our response into, or we can use a free online to do this for us. I personally like to use https://json2csharp.com/. In my experience I have just been able to copy and paste JSON straight into their tool and it provide a set of ready made classes that I can use.

The only thing we need to do is rename the root object it provides into something meaningful. In this case, we will call this class BookInformation. Create a Classes folder in the root of the project and then create a class in this folder, giving it the name BookInformation. Paste the C# classes provided from the online tool and rename the Root class to BookInformation.

Now we will create the service that will fetch this data and serialize it into this class. Create a folder called Services and create a class called GoogleBooksAPIDataService. Paste the following code inside:

public class GoogleBooksAPIDataService
{
    private readonly HttpClient httpClient;
    private readonly string requestUri;

    public GoogleBooksAPIDataService(HttpClient httpClient)
    {
        this.httpClient = httpClient;
        requestUri = "https://www.googleapis.com/books/v1";
    }

    public async Task<BookInformation> GetBookInformation(string isbn)
    {
        var response = await httpClient.GetStringAsync($"{requestUri}/volumes?q=isbn:{isbn}");
        var bookInformation = JsonSerializer.Deserialize<BookInformation>(response);
        return bookInformation;
    }
}

As you can see, we are using .NET’s HttpClient class in order to perform a call to the API. We fetch the response as a string and use JsonSerializer in order to morph this text into the much more usable BookInformation class we obtained earlier. We mustn’t forget to register this service in the Configuration method of the Startup class, along with HttpClient so that we can use dependency injection to inject these classes where needed:

Next we will create a component that can display some of this information. In the Components folder, add a new razor component, giving it the name Book. Paste the following code inside:

@if (BookInformation != null && BookInformation.items != null)
{
    var imgSrc = BookInformation.items[0].volumeInfo.imageLinks.thumbnail;

    <p>
        @BookInformation.items[0].volumeInfo.title
    </p>

    <p>
        @BookInformation.items[0].volumeInfo.description
    </p>

    <img src="@imgSrc" />
}

@code {
    [Parameter] public BookInformation BookInformation { get; set; }
}

This component accepts a parameter of BookInformation which, if a value is present, will display some of the information we obtained from Google’s Books API. 

Finally, modify the home page to incorporate this component:

@page "/"

<h1>Welcome to BookMate!</h1>

<BarcodeScanner OnISBNDetected="GetBookInformation" />

<Book BookInformation="bookInformation" />

@code {
    [Inject] GoogleBooksAPIDataService BooksAPI { get; set; }

    private BookInformation bookInformation = null;

    private async Task GetBookInformation(string isbn)
    {
        bookInformation = await BooksAPI.GetBookInformation(isbn);
    }
}

We have injected the API service into our component and added a reference to BookInformation. We have also modified our event callback from earlier so that when an ISBN is detected, it will perform a call to the API, giving BookInformation a value which in turn will display some of its information. 

And there we have it, a small but cool application. If you’ve reached this far, first of all thank you! Secondly, if you have any suggestions at all on how we could use this functionality please let me know. I would love to hear your ideas. 

2 thoughts on “How to Build a Book Barcode Scanner in Blazor C#”

  1. Thanks so much for this! I’m building a book checkout system for a little English school in Korea, and this solved all the technical problems in one page, probably saving me a couple days’ hard work.

    A note: There’s a google books dotnet API. I haven’t used much of it yet, and don’t really need to– BUT it includes a full description of the Volumes and Volume objects that the JSON API returns, which I used instead of lifting it from a JSON result (which is usually incomplete).

    If you make a netstandard2.0 class library, you can search for “google books” in the Nuget package manager. I did find a little hiccup with the JSON setting:

    JsonSerializer.Deserialize(response, new JsonSerializerOptions(JsonSerializerDefaults.Web)); // NOTE: the Web option is the magic juice that made it work.

  2. Hi Benjamin, thank you for your kind comment and sentiment. I am so glad that the post has been able to help someone. Also, thanks for the tip on the Google Books API too, I’ll be sure to look into that!

Comments are closed.

Scroll to Top