Click here to Skip to main content
14,544,533 members

.NET Core Web API : The Least You Need To Know (part 2 of 2)

Rate this:
4.79 (4 votes)
Please Sign up or sign in to vote.
4.79 (4 votes)
20 May 2020CPOL
Post to your Web API via the XmlHttpRequest - learn how to set up headers, data for body, etc.
Now you can see all in one place how to get your .Net Core Web API up and running and accepting posted data.

Introduction

This is the second and concluding part of the two-part article series. 

You can read part one at :.NET Core Web API : The Least You Need To Know (part 1 of 2)[^]

In this article, we will cover:

  1. Creating basic HTML page to use to submit Post to WeatherForecastController
  2. Altering the dotnet Web API to return static pages (serve HTML).
  3. Basic JavaScript which will contain the code that creates the XmlHttpRequest (XHR)
  4. Configuring the XHR to Post with data in the body and appropriate headers.

In The First Article

In the first article we created the basic Web API template project and learned how to post to it using PostMan^

Now, we take all that we learned there and apply it to creating a web page that can post JSON to our Web API.

Let's jump right in and create our HTML page that we will use to post to our web controller.

Add SubFolder and index.htm

The first thing I do is add a new subfolder to my project named wwwroot.  I am adding that folder name because it is the name of the folder where I deploy my files on my hosted web site where I can deploy my Web API.

You will also see that the default server that dotnet runs will automatically know about wwwroot and serve the index.htm file directly from that directory.

Next, I add a new file named index.htm and add our basic HTML that will provide us with a button we can click to submit our post to the Web API.

Here's the entire HTML file.  It is very simple and keep in mind that this entire example uses vanilla JavaScript (no need for external JavaScript libraries) so all of the code is very simple.

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>XHR Poster</title>
        <meta charset="utf-8">
        <script src="main.js"></script>
        <style>
            body{padding: 5px 15px 5px 15px;}
        </style>
    </head>
    <body>
        <label for="tempC">Temp (C)</label>
        <input id="tempC" type="number" value="20">
        <button onclick="postData()">Post TempC</button><br>
        <label for="tempF">Temp (F)</label>
        <input id="tempF" type="text">
    </body>
</html>

That page renders the following in the browser.

simple html test page

Four Take-Aways From HTML

Since it is self-explanatory, I won't explain all of the HTML but I will point out four important things to notice:

  1. main.js which is loaded at the top contains all of our JavaScript code that provides our needed functionality
  2. There is a number input which allows the user to set the TempC value which is posted to the Web API (represents the value in Celsius)
  3. The text input which displays the output value of TemperatureF which is returned by the Web API (represents the value in Fahrenheit)
  4. The functionality of the page is fired by clicking the button which calls the JavaScript method postData() which is found in the main.js

If you're following along, go ahead and add a main.js file to the /wwwroot folder now.  Here's a snapshot of what all of the files look like in my Visual Studio Code (VSC) IDE (Integrated Development Environment).

VSC file view

If you look closely you will see that the /wwwroot directory contains the two files (index.htm and main.js).

dotnet run : Start Your Web Server

Let's run dotnet so it'll compile the Web API again and start up the web servers for us so we can attempt to get it to serve the index.htm file.  It'll cause an error but it'll be instructive.

To start the Web API go to your console window (all of this is detailed in Part 1 of this series) and type the command :

$>dotnet run

When you do that the server will start on the default host and port (localhost:5001) so we can attempt to load the index.htm.

Client Code and Server Code (Web API)

Keep in mind that now our project contains both client code (index.htm and main.js) and Server Code (written in C#).  However, the dotnet engine basically ignores our HTML and JavaScript.  It knows that it doesn't need to compile that code and it only needs to work with the C# code it finds.  The other thing that dotnet does do however, is start a nice web server for you.

Try To Load index.htm

Try this URL and see if index.htm loads: https://localhost:5001/index.htm

Here's what you'll see.  It is a completely blank page.

blank web page

Your first thought might be that you need to change the URL to include the subdirectory /wwwroot like : https://localhost:5001/wwwroot/index.htm

I thought maybe that was the problem when I attempted to work through this.

You also may think there is at least some skeleton HTML in there but I assure you there is not.  When you hit that URL (which is non-existent to your Web API) the server responds with nothing. 

If you are attempting this for the first time, it can be really confusing because you may think something else is wrong.  It's a very difficult problem to search for too, because there is no error value or error message.

The problem is that the Web API is not set up to serve static pages like index.htm.  Let's change that.

Change Web API to Serve Static Pages

By default a Web API project will not serve a static page such as index.htm.  That probably makes sense because this is a Web API we are creating.  However, if you want to be able to test your Web API and not get into all kinds of difficulties with CORS (Cross-Origin Resource Shares) then it is nice to be able to know how to change the Web API and it is very easy.

Change the Startup.cs

To do this, we simply need to open up the Startup.cs and add a new service to start running on the dotnet app.

When you open up the Startup.cs and look inside the Configure() method, you are going to see that a number of services are already loaded up.  Before we change the code it will look something like the following:

// This method gets called by the runtime. Use this method to configure 
// the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
 
            app.UseHttpsRedirection();
 
            app.UseRouting();
 
            app.UseAuthorization();
 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

You can see that the project template has already added a number of services to start running on the app object already.  Those services provide certain functionality like routing (UseRouting()) and HttpsRedirection() etc.

We just need to add one line of code which will add the static page service.

Let's add one line right after the current line that has:

app.UseAuthorization();

Our new line of code will be:

app.UseStaticFiles();

It looks like this in VSC:

app.UseStaticFiles()

Once you make that change, go back to your console and stop the Web API (CTRL-C) and then start it again via $> dotnet run

Now, we can try the URL again and the page will be served properly:

https://localhost:5001/index.htm^

That's better.

static page loads properly

Now, we are set up to try out some JavaScript code.  But now we need to get our JavaScript just right with our XmlHttpRequest (XHR).  We'll do all of that work in the main.js file.

Configuring the XHR In JavaScript

We need to do four things:

  1. Instantiate the built-in (JavaScript / Browser provided XHR object)
  2. add an event listener to the XHR object for when the Asynchronous request completes (load event)
  3. add an event listener to the XHR object that will run if an error occurs
  4. add a variable to hold the url we will post to

Here's the four lines of code that I've placed at the top of main.js

var xhr = new XMLHttpRequest();
xhr.addEventListener("load", transferComplete);
xhr.addEventListener("error", transferFailed);
var url = "http://localhost:5001/WeatherForecast/"

When the XHR object load event occurs it will call a method named transferComplete() that we will define further down in the JavaScript code.

If the XHR object error event occurs then it will call a method named transferFailed() that will be defined further down in our JavaScript code.

The User Starts the Process By Clicking the Button

I will show you those two methods in a moment, but first let's take a look at the code that is fired when the user clicks the [TempC] button.

Remember, we wired up the button in the HTML so that onclick="postData()".  That method is defined in our main.js JavaScript.

Here's the entire method from VSC with line numbers so we can discuss what is happening.

postData() Method: Line-by-Line

postData() method

Since we've already instantiated the xhr object and set the url, we now just need to call the xhr object's open() method.

We provide two parameters to the open() method.

  1. the HTTP action (Get) as a string
  2. the URL that we are posting to

yyyy-mm-dd date format

On line 8 we set a local variable up to hold a date string.  Notice that this date is in the format of yyyy-mm-dd.

If you use another format the Web API may not be able to transform the string into a date on the server side and it may fail. 

Line 9

We call the browser method document.querySelector() to get a reference to the number input control and get its current value.    That way the user can dynamically change the value to submit different values to the Web API.  We store the current value in a local variable for later use.

Line 10 is just a console.log() to the browser console so we can see what is happening.

Line 11: JSON Object

On line 11 we create a new variable named weather and we initialize it with JSON values.  Each of the names in the JSON will become a property on the local weather object.  I do this because it makes it far easier to post JSON to the Web API.  Of course, each property name in the weather object matches the property names in our WeatherForecast domain object (defined in C# in the Web API -- see Part 1 of this article for more information).

Line 12: Explicitly Convert Value to Number

One line 12 I add a new property named TemperatureC to the weather object that I've already created.  JavaScript is a dynamic language and allows this type of manipulation on its objects.  You can dynamically add a new property simply by referencing it on the object and setting it to a value.  I add this property name (TemperatureC) because that is the expected name on the domain object on the Web API side.

Notice that I also explicitly coerce the local variable tempC value (which is a string) into a JavaScript Number type (which is a Double -- numeric high precision).  That's because I found that since the Web API is expecting an Int32 for TemperatureC that it seems to fail if the value comes in as a String (there is no automatic conversion to the Int32 from the JSON object's string).

Line 13 is another console.log() just to check the format of the Date property.

Set the Request Header

On line 14 we have to set the Content-Type request header on our XHR object or the Web API will fail with a 415 error just as it did when we posted to the URL using PostMan (see article Part 1).

XHR Send

Finally on line 15 we call the XHR send() method which will actually post to the Web API url.

However, we want to post the JSON string that represents our weather object and to do that we call a built-in browser method called JSON.stringify() which will turn the weather object into the appropriate JSON string.

But, this is just the request that gets the Web API WeatherForecastController default Post method to fire.  We need to do some work, when the request completes and our JavaScript code is alerted that the load event has fired.  That's where the event handler that we bound earlier comes in to play.

transferCompleted() Line-by-line

Here's the extremely simple transferCompleted() method that we defined and wired up to run when the XHR object's load event fires. 

transferCompleted()

The first two lines just output to the browser console so we can see what is coming back from the Web API.  We now that the default Post() method of the Web API returns the Fahrenheit temperature that represents the Celsius temperature that we posted to the method (See Part 1 for more).

XHR Response Property

The xhr object's response property holds the data that is returned by the Web API.  In our case that is just an integer value that represents the Fahrenheit temperature.  Normally you'd return an entire JSON object that represents something more complex than this, but for our tests we are just returning the integer value.

On line 21 we simply use the document.querySelector() method to get a reference to the tempF text control and we set its value to the value returned by the Web API (xhr.response).

final output

Process Overview

That's it.  Now when the user selects a value in the number control and clicks the button, the Web API is called and it returns the associated Fahrenheit temperature and displays it in the tempF text box.

It all works because we set up our XHR object properly with the :

  1. correct URL
  2. proper Content-Type header
  3. correct JSON (representing a WeatherForecast domain object)

If you get any one of those incorrect you will see a very basic error in your browser console letting you know that something went wrong.

Grab the Code and Try It

I've posted the final code at the top.  Grab the code and try it for yourself.  Now you can model your Web API and JavaScript requests on this and get yourself started with this technology and the information is all compiled in one place.

History

05-20-2020 : article published

License

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

Share

About the Author

raddevus
Software Developer (Senior) RADDev Publishing
United States United States
Roger has worked in IT for over 25 years in numerous roles (Technical Support, Quality Assurance, Capacity & Performance Engineering and Software Development).
During that time, he has recognized that software often just becomes another layer of work that the user has to wade through.
Sometimes technical documentation is like that too: so confusing and complex that it wastes developers' time.
That's why when he writes his books like Programming Windows 10 Via UWP and his articles (Practical Electronics For Makers) he strives to explain things in the shortest available space with the simplest language possible. Often that means, writing in a tutorial style with numerous images to help guide the user.
He believes the best guiding principle is Einstein's famous quote: "Everything should be made as simple as possible, but not simpler."

Comments and Discussions

 
QuestionMessage Closed Pin
20-May-20 20:51
MemberMember 1192862820-May-20 20:51 
Questionimage problem Pin
raddevus20-May-20 12:30
mvaraddevus20-May-20 12:30 
AnswerRe: image problem Pin
raddevus20-May-20 12:33
mvaraddevus20-May-20 12:33 

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.

Article
Posted 20 May 2020

Stats

3.3K views
59 downloads
13 bookmarked