Understanding HttpApi.Client Project & Remote Services in an ABP Based Application
When working with ABP Framework to build layered applications, developers often encounter the project within their solution structure. While this project plays a crucial role in the overall architecture, its purpose and implementation can initially seem complex or unclear. This comprehensive guide will demystify the project, explaining its fundamental purpose and demonstrating practical usage scenarios that will enhance your understanding of ABP's remote service capabilities.
Solution Structure of an ABP-Based Layered Application
Throughout this article, I'll demonstrate concepts using a sample MVC application. However, it's applicable for other UIs.
When you scaffold a new layered application using ABP Framework, the generated solution contains several projects that follow clean architecture principles. For instance, when creating an application with MVC/Razor Pages UI, your solution will include the following project structure:
The solution architecture is built around well-defined layers, each serving specific responsibilities. You'll find the layer (consisting of and projects), the layer (encompassing and projects), the layer (in this example, configured with MongoDB), and the presentation layer represented by the project.
Beyond these four foundational layers, the solution includes several specialized projects that enhance functionality and development workflow:
: A dedicated console application responsible for database schema migrations and initial data seeding operations.
: This project is used to define your API Controllers. (ABP automatically creates controllers based on your application services. See Auto API Controllers documentation for more details.)
: We will focus on this project in this post but basically, this project is used to generate client-side proxies that enable seamless consumption of your application's APIs.
While most of these projects align with familiar layered architecture patterns, the project often raises questions among developers new to ABP (or layered architecture in general). Let's dive into the project and understand its purpose.
HttpApi.Client Project
The ABP Framework documentation provides a foundational definition of the project:
"This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications"
While this definition captures the essence of the project, it only scratches the surface of its capabilities and practical applications. To truly understand its value, let's examine the project's internal structure and configuration.
When we look at the project and examine the class, we can see its dependencies:
view rawBookStoreHttpApiClientModule.cs hosted with ❤ by GitHub
Looking at the module configuration, we can see that the project depends on several ABP modules. However, the key dependency is on . This means the project only has access to the contracts (interfaces and DTOs) defined in the project, and nothing else.
The project only knows about interfaces like , but not their actual implementations like . This keeps client code cleanly separated from the backend implementation details.
The primary purpose becomes clear: the project enables client applications to consume application services through HTTP requests without creating tight coupling to the core business logic implementations. This approach facilitates distributed architectures where client applications (such as Blazor WebAssembly apps, mobile applications, or third-party integrations) can interact with your backend services through well-defined contracts.
Consider a practical scenario: when developing a Blazor WebAssembly application, you can reference the project and utilize application services from the client-side without depending on the core implementation. The only requirement is configuring the backend application's URL, and ABP handles the rest of the communication infrastructure.
The module's method contains another crucial configuration:
The method scans the assembly, finds all service interfaces, and creates proxy classes for them automatically. This feature is called Dynamic C# API Client Proxies. When you define interfaces in the project, ABP creates the proxy implementations for you at runtime - you don't need to write any proxy code yourself.
Seeing HttpApi.Client in Action
Now that we understand the theoretical foundation, let's observe the practical implementation of HTTP API client proxies. The core concept revolves around creating proxy implementations that communicate with remote HTTP APIs. When you define an interface, ABP automatically generates a corresponding proxy class (conceptually ) that handles the HTTP communication seamlessly, allowing you to work with remote services as if they were local implementations.
To demonstrate this functionality in a real-world scenario, we'll examine the test project included in our solution:
This test project serves as a practical demonstration of client proxy usage. It's implemented as a console application with a deliberately minimal dependency structure, it only references the project:
Also, this test project contains a simple class that is used to show the static proxy usage:
This service implementation demonstrates several key concepts. We're injecting (from the Account module) and (from the Identity module) interfaces directly into our service. The remarkable aspect is that despite having no direct dependencies on the actual Account and Identity module implementations, these services function seamlessly through the proxy mechanism.
So how does this magic work behind the scenes?
The answer lies in the configuration. Examining the file reveals the critical connection:
This configuration establishes two fundamental aspects of the remote service communication:
Remote Service Configuration: The property under the remote service name specifies https://localhost:44352/ as our backend application endpoint. This is where our web application exposes the HTTP API endpoints that the proxies will consume. The name here matches with the constant we defined in the module.
Identity Client Configuration: The authentication settings enable the client to authenticate with the remote service using the configured credentials.
Once you provide the remote service URL, ABP handles the HTTP communication, serialization, and proxy generation automatically. When you inject an interface like , ABP creates a proxy that communicates with the remote endpoint.
To observe the dependency on remote services, try running the test project without starting the web application. You'll encounter the following error:
This error clearly demonstrates the HTTP-based nature of the communication. The proxy attempts to send requests to https://localhost:44352/ using the remote service configuration but fails because the backend service isn't running.
However, when both the web application and test project are running simultaneously, you'll see successful communication:
This output confirms that the and interfaces are successfully communicating with the remote backend through HTTP requests, retrieving real data from the running application.
Recap of the Dynamic C# API Client Proxies
Let's consolidate our understanding of how the dynamic proxy system operates by tracing the configuration flow from definition to execution.
You might recall that we configured the remote service URL in the file, but where exactly does ABP utilize this configuration? The connection becomes apparent when we revisit the class and examine this constant definition:
This constant plays a crucial role in linking the module configuration to the actual remote service settings. It's referenced in the method:
This configuration establishes the binding between the assembly containing your application service interfaces and the named remote service that will handle the HTTP communication.
Following this module configuration, we define the corresponding remote service settings in our client application's file:
This creates a complete configuration chain: the declares a dependency on the "Default" remote service, and the file provides the concrete endpoint information for that service name.
When configured, ABP automatically creates proxy classes for your application service interfaces. These proxies handle all HTTP communication behind the scenes, allowing you to work with remote services as if they were local classes.
Understanding the RemoteServices
As we explored the basic remote service configuration, let's look at an important aspect - the section in the configuration. While we've been using the remote service name in our examples through the class, ABP's architecture is more flexible than that.
Remember our class where we injected the and interfaces from the and modules? This is where ABP's modular architecture shines - each module can define its own remote service identifier, allowing for more sophisticated scenarios where different modules can be configured independently or even hosted on separate servers.
The modules we've been working with, and ; each define their own unique remote service names. To discover these module-specific identifiers, ABP provides a built-in API definition endpoint. You can query to retrieve comprehensive information about all available APIs and their associated module configurations.
When you examine the API definition response, you'll find detailed information about each module's remote service configuration. For the module, specifically the services we've been using like , the remote service name is defined as :
Similarly, the module, which provides services like , uses the remote service name :
This module-specific naming convention enables more granular control over service communication. Instead of routing all requests through a single remote service, you can configure each module to communicate with its dedicated endpoint.
Let's implement this more sophisticated configuration by updating our file to use module-specific remote service names:
This configuration demonstrates ABP's flexibility in handling distributed architectures. While both modules currently point to the same , in a real-world microservices architecture, you might have:
The Account services running on https://account-service.company.com/
The Identity services running on https://identity-service.company.com/
Your core business services running on https://main-api.company.com/
When you run the test project with this updated configuration, the behavior remains identical to the previous setup:
So, if you seperate your modules into different services, you can use their remote service names to consume the application services from the client-side and with a single configuration, you can use different remote service URLs for different modules.
Summary
In this post, we explored the project and how to use it to consume application services from the client-side. We also covered the configuration in and how to use different remote service URLs for different modules.
I hope you found this post helpful. Feel free to subscribe for more updates and share your thoughts in the comments below.
Thank you for reading :)
This article was written by Engincan Veske