Skip to main content
All docs
V23.2

Make HTTP Requests to the Web API from .NET Applications

  • 13 minutes to read

You can send requests to a Web API service from any .NET application with the HttpClient library. Use the OData syntax to build requests.

Note

If you target .NET for your backend API, be sure to register your FREE copy of our Web API Service. The Solution Wizard scaffolds an OData v4 Web API Service (.NET 6+) with integrated RBAC authorization, and CRUD operations powered by EF Core and our XPO ORM library. Among its numerous capabilities, our built-in Web API Service filters out secured server data based on permissions granted to users. Advanced/enterprise functions include audit trail, endpoints to download reports, attach files, check validation, obtain localized captions, etc. To use the free Solution Wizard (which creates the Web API Service), run the Universal Component Installer from the DevExpress Download Manager or Free 30-Day Trial pages.

To manage users, roles and security permissions at runtime in WinForms, WebForms, and ASP.NET Core Blazor administrative UI/portal, use our Cross-Platform .NET App UI (XAF): Getting Started Tutorial | Demos.

XAF's Blazor UI Demo, DevExpress

See the following topics for more information on OData query options:

The examples below send requests to the Web API service available at the following address: https://localhost:44319/.

Authenticate with JSON Web Tokens (JWT)

To obtain the JWT Authentication token for further data requests, send a request to the following endpoint: api/Authentication/Authenticate. The following example uses “Sam” as a user name and an empty password:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
    }
}

Note

For more information about cookie or JWT authentication in JavaScript instead of .NET, review the JavaScript — Consume the DevExpress Backend Web API with Svelte (Part 5. Authenticate Users and Protect Data) article and the corresponding GitHub example (the src/hooks.server.js file in particular).

Authenticate with OAuth2

If your Web API Service application uses OAuth2 authentication, follow the steps below to obtain an access token. The described steps assume that a user, on whose behalf you authenticate in the Web API Service, is already registered in the service’s Security System. Refer to the following topic for more information on how to configure OAuth2 on the Web API Service side: Configure the OAuth2 Azure Authentication for the Web API.

Register the ITokenAcquisition Service

Add the following lines to your client application’s startup code to register the ITokenAcquisition service:

builder.services.AddMicrosoftIdentityWebApi(Configuration, configSectionName: "Authentication:AzureAd", jwtBearerScheme: "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches(); // Add this line to register the `ITokenAcquisition` service

Acquire the Access Token

Access the ITokenAcquisition service through Dependency Injection or use the application’s ServiceProvider to resolve it:

private readonly ITokenAcquisition _tokenAcquisition;

public YourContextConstructor(ITokenAcquisition tokenAcquisition {
    _tokenAcquisition = tokenAcquisition;
}

After that, use the ITokenAcquisition.GetAccessTokenForUserAsync method to acquire the access token:

var scopes = new[] { "scope1", "scope2" }; // Replace with the required scopes
var oAuthToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);

Create and Send a Request

Create an HttpRequestMessage instance and assign the obtained access token to the authorization header:

string endPointAddress = "https://your-endpoint.com/api/endpoint"; 
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, endPointAddress);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", oAuthToken);

using var responseMessage = await _httpClient.SendAsync(httpRequestMessage);

Operate with Business Objects

Get Business Objects

The following code retrieves the LastName and Email fields of the Employee business object where FirstName equals “Mary”:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Send a request to fetch data.
            string requestAddress = "https://localhost:44319/api/odata/Employee";
            var employees = await httpClient.GetStringAsync($"{requestAddress}?$filter=FirstName eq 'Mary'&$select=LastName,Email");
            Console.WriteLine(employees);
        }
    }
}
Result
{"@odata.context":"https://localhost:44319/api/odata/$metadata#Employee(LastName,Email)",
"value":[{"LastName":"Tellitson",
            "Email":"Mary_Tellitson@example.com"}]}

Create a Business Object

The code below adds a new Employee instance with the FirstName field set to “Mary” and the LastName field set to “Gordon”:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();
            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Pass data to the Web API service. 
            StringContent dataHttpContent = new StringContent(@"{ ""FirstName"": ""Mary"", ""LastName"":""Gordon"" }", Encoding.UTF8, "application/json");
            var dataResponse = await httpClient.PostAsync($"{requestAddress}", dataHttpContent);
            Console.WriteLine(dataResponse.StatusCode);
        }
    }
}

Result (the dataResponse.StatusCode value): 201 Created

Get a Reference Object

You can use one of the following techniques:

Technique 1 (Expand Query Parameter)
The $expand OData query parameter allows you to obtain a reference business object together with the main object.
Technique 2 (Ref Endpoint)
The $ref endpoint allows you to obtain a reference business object without its main object.

Note

If you apply the AutoExpand attribute to a referenced property, you do not need to explicitly specify the $expand parameter in an OData query because it is automatically loaded and its data is included in the API response.

Technique 1 (Expand Query Parameter)

The example below uses $expand to get an Employee object with its related Department object:

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Send a request to fetch data.
            string requestAddress = "https://localhost:44319/api/odata/Employee";
            var employees = await httpClient.GetStringAsync($"{requestAddress}?$filter=FirstName eq 'Mary'&$select=LastName,Email&$expand=Department");
            Console.WriteLine(employees);
        }
    }
}
Result
{"@odata.context":"https://localhost:44319/api/odata/$metadata#Employee(LastName,Email,Department())",
 "value":[{"LastName":"Tellitson",
           "Email":"Mary_Tellitson@example.com",
           "Department":{
                "Oid":"6eff292f-f871-4237-a22c-8a50aa747ea3",
                "Title":"Development Department",
                "Description":"The Information Technology Department manages the company's information infrastructure and online assets.",
                "Location":"Building 2",
                "Office":"205"}
        }]
}

The $expand parameter can be applied to more than one level of related business objects. The following example retrieves a data chain that consists of two related business objects:

// ...
string requestAddress = "https://localhost:44319/api/odata/Department";
var departments = await httpClient.GetStringAsync($"{requestAddress}?$select=Title&$expand=Employees($select=FirstName,LastName;$expand=Tasks($select=Subject))");
// ...
Result
{"@odata.context": "https://localhost:44319/api/odata/$metadata#Department(Title,Employees(FirstName,LastName,Tasks(Subject)))",
 "value": [{ "Title": "Human Resources",
             "Employees": [{ "FirstName": "Angela",
                             "LastName": "Gross",
                             "Tasks": [{ "Subject": "Create 2022 R&D Plans"},
                                       { "Subject": "Submit D&B Number to ISP for Credit Approval"},
                                       { "Subject": "Deliver R&D Plans for 2022"}]
                           },
                           { "FirstName": "Barbara",
                             "LastName": "Faircloth",
                             "Tasks": [{ "Subject": "Subject": "Deliver R&D Plans for 2022"},
                                       { "Subject": "Submit D&B Number to ISP for Credit Approval"},
                                       { "Subject": "Create 2022 R&D Plans"}]
                            }]
           },
          { "Title": "Purchasing",
            "Employees": [{ "FirstName": "Ernest",
                            "LastName": "Webb",
                            "Tasks": [{ "Subject": "Submit D&B Number to ISP for Credit Approval"},
                                       { "Subject": "Deliver R&D Plans for 2022"}]
                           },
                           ... 
                         ]
          }]
}

The default max expansion depth equals two. You can change this parameter as described in the following topic: Change the Expansion Depth for Related Business Objects.

Technique 2 (Ref Endpoint)

The example below gets the Employee’s Department reference object:

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Send a request to fetch data.
            string requestAddress = "https://localhost:44319/api/odata/Employee/1/Department/$ref";
            // or 
            // string requestAddress = "https://localhost:44319/api/odata/Employee(1)/Department/$ref";
            var department = await httpClient.GetStringAsync(requestAddress);
            Console.WriteLine(department);
        }
    }
}
Result
{"@odata.context":"https://localhost:44319/api/odata/$metadata#Department/$entity",
"ID":1,
"Title":"Development Department",
"Office":"205","Location":"Building 2",
"Description":"The Information Technology Department manages the company's information infrastructure and online assets."}

Get an Associated Collection

You can use one of the following techniques:

Technique 1 (Expand Query Parameter)
The $expand OData query parameter allows you to obtain objects from an associated collection together with the main object.
Technique 2 (Ref Endpoint)
The $ref endpoint allows you to obtain objects from an associated collection without its main object.

Technique 1 (Expand Query Parameter)

The following example gets LastName, Email, and the related Tasks collection of the Employee business object where FirstName equals “Mary”:

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Send a request to fetch data.
            string requestAddress = "https://localhost:44319/api/odata/Employee";
            var employees = await httpClient.GetStringAsync($"{requestAddress}?$filter=FirstName eq 'Mary'&$select=LastName,Email&$expand=Tasks");
            Console.WriteLine(employees);
        }
    }
}
Result
{"@odata.context":"https://localhost:44319/api/odata/$metadata#Employee(LastName,Email,Tasks())",
 "value":[{"LastName":"Tellitson",
           "Email":"Mary_Tellitson@example.com",
           "Tasks":[{"Oid":"b958f20a-118d-4af0-b249-94445608549d",
                     "Subject":"2022 Brochure Designs",
                     "DueDate":"2022-01-15T00:00:00+04:00",
                     "StartDate":"0001-01-01T00:00:00Z",
                     "Status":"Deferred",
                     "PercentCompleted":0,
                     "Priority":"Normal"},
                    {"Oid":"7de87fc8-4dc0-4b76-82b3-18dffdc61ba4",
                     "Subject":"Review Benefits",
                     "DueDate":"2021-10-02T00:00:00+04:00",
                     "StartDate":"2021-09-12T00:00:00+04:00",
                     "Status":"Completed",
                     "PercentCompleted":100,
                     "Priority":"Normal"},
                    {"Oid":"67d36cda-a261-489f-afa9-c8ac43e1c2ea",
                     "Subject":"Lunch Potluck",
                     "DueDate":"2021-10-03T00:00:00+04:00",
                     "StartDate":"0001-01-01T00:00:00Z",
                     "Status":"Deferred",
                     "PercentCompleted":0,
                     "Priority":"Low"}
                    ]
        }]
}

Technique 2 (Ref Endpoint)

The example below gets the Employee’s Tasks collection:

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static async Task Main(string[] args) {
            HttpClient httpClient = new HttpClient();

            // Obtain a JWT token. This example uses "Sam" as a user name and an empty password.
            StringContent httpContent = new StringContent(@"{ ""userName"": ""Sam"", ""password"": """" }", Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync("https://localhost:44319/api/Authentication/Authenticate", httpContent);

            // Save the token for further requests.
            var token = await response.Content.ReadAsStringAsync();

            // Set the authentication header. 
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            // Send a request to fetch data.
            string requestAddress = "https://localhost:44319/api/odata/Employee(1)/Tasks/$ref";
            // or 
            // string requestAddress = "https://localhost:44319/api/odata/Employee/1/Tasks/$ref";
            var tasks = await httpClient.GetStringAsync(requestAddress);
            Console.WriteLine(tasks);
        }
    }
}
Result
{"@odata.context":"https://localhost:44319/api/odata/$metadata#DemoTask",
"value":[
    {
        "ID":8,
        "Subject":"Approve Overtime Pay",
        "Description":"Brett, the overtime I submitted was not paid and I'm being told it was not approved. I thought you approved this. What is the problem?\r\nBrett Wade: I did approve it. It was an error in payroll. Trying to figure it out.",
        "DueDate":"2022-04-22T00:00:00+04:00",
        "StartDate":"2022-03-28T00:00:00+04:00",
        "PercentCompleted":0,
        "Status":"Completed",
        "Priority":"Normal",
        "ActualWorkHours":15,
        "EstimatedWorkHours":19
    },
    {
        "ID":9,
        "Subject":"Move Inventory to New Warehouse",
        "Description":"Robin, you are point person to get all inventory moved to the new warehouse location. You can hire temp workers if needed.",
        "DueDate":"2022-04-24T00:00:00+04:00",
        "StartDate":null,
        "PercentCompleted":0,
        "Status":"NotStarted",
        "Priority":"Low",
        "ActualWorkHours":0,
        "EstimatedWorkHours":10
    },
    {
        "ID":10,
        "Subject":"Shipping Label Artwork",
        "Description":"Kevin wants new shipping labels and I cannot print them without the artwork from your team. Can you please hurry and send it to me.\r\nMorgan Kennedy: Send me the specs and I will work on it when I can.",
        "DueDate":"2022-04-24T00:00:00+04:00",
        "StartDate":"2022-04-19T00:00:00+04:00",
        "PercentCompleted":0,
        "Status":"InProgress",
        "Priority":"High",
        "ActualWorkHours":19,
        "EstimatedWorkHours":12
    }
]}

Assign an Object to a Reference Property

The example below sets an Employee’s Department reference property to a Department object:

Technique 1 (In Body) - XPO Only

string requestAddress = "https://localhost:44318/api/odata/Employee/1";
string jsonBody = @"{ ""Department"": ""1""}";
// or
// string jsonBody = @"{ ""Department"": { ""Oid"": ""1"" }}";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PatchAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

To clear a Reference property, send a null value. The example below assigns null to an Employee’s Department property:

string requestAddress = "https://localhost:44318/api/odata/Employee/1";
string jsonBody = @"{ ""Department"": null }";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PatchAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

Technique 2 (Ref Endpoint)

string requestAddress = "https://localhost:44319/api/odata/Employee/1/Department/$ref";
string jsonBody = "{\"@odata.id\":\"https://localhost:44319/api/odata/Department/1\"}";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

Add an Object to a Collection

Technique 1 (In Body) - XPO Only

The example below adds a DemoTask object to an Employee’s Tasks collection:

string requestAddress = "https://localhost:44319/api/odata/Employee/1";
string jsonBody = @"{ ""Tasks"": [ { ""Oid"": 1 } ] }";
var response = await httpClient.PatchAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

Technique 2 (Ref Endpoint)

The example below adds a DemoTask object to an Employee’s Tasks collection:

string requestAddress = "https://localhost:44319/api/odata/Employee/1/Tasks/$ref";
string jsonBody = "{\"@odata.id\":\"https://localhost:44319/api/odata/DemoTask(1)\"}";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

The example below removes the Employee’s Department reference property value:

string requestAddress = @"https://localhost:44319/api/odata/Employee/1/Department/$ref?
                          $id=https://localhost:44319/api/odata/Department/1";
// or 
// string requestAddress = @"https://localhost:44319/api/odata/Employee(1)/Department/$ref?
//                           $id=https://localhost:44319/api/odata/Department(1)";
var response = await httpClient.DeleteAsync(requestAddress);
Console.WriteLine(response)

Result (the dataResponse.StatusCode value): 204 No Content

Remove an Object from a Collection

The example below removes the DemoTask object from the Employee’s Tasks collection:

string requestAddress = @"https://localhost:44319/api/odata/Employee/1/Tasks/$ref?
                          $id=https://localhost:44319/api/odata/DemoTask/1";
// or 
// string requestAddress = @"https://localhost:44319/api/odata/Employee(1)/Tasks/$ref?
//                           $id=https://localhost:44319/api/odata/DemoTask(1)";
var response = await httpClient.DeleteAsync(requestAddress);
Console.WriteLine(response)

Result (the dataResponse.StatusCode value): 204 No Content

Modify an Object Assigned to a Reference Property (XPO Only)

The example below modifies the Department object assigned to the Employee’s Department reference property:

string requestAddress = "https://localhost:44319/api/odata/Employee/1";
string jsonBody = @"{ ""Department"": { ""Office"":""504""} }";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PatchAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

Modify Objects Added to an Associated Collection (XPO Only)

The example below modifies the DemoTask object with Oid=1 from the Employee’s Tasks collection. If the collection does not contain the DemoTask object with this Oid, this object will be added:

string requestAddress = "https://localhost:44319/api/odata/Employee/1";
string jsonBody = @"{ ""Tasks"": [{ ""Oid"": ""1"", ""Subject"":""New subject""} ]}";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PatchAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 204 No Content

Call an Object’s Action Method

The example below demonstrates how to call an object’s method decorated with an ActionAttribute. See the Add Endpoints for Business Object Methods topic for more information on this feature and how to enable it.

string requestAddress = "https://localhost:44319/api/odata/Task/b1fea24f-4b60-4cd9-2158-08db8797bd56/Postpone";
string jsonBody = "{\"Days\": 7}";
StringContent content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(requestAddress, content);
Console.WriteLine(response);

Result (the dataResponse.StatusCode value): 200 OK

Review GitHub Examples

See Also