Skip to content

Tutorial 04 09 Add Controller Class

Matt Linder edited this page Jun 30, 2023 · 21 revisions

Harmony Core Logo

Tutorial 4: Exposing Endpoints for Traditional Bridge Routines

You're almost there! Only two more steps, the next of which is to actually expose your Traditional Bridge routines as endpoints of your web service.

There are several ways this can be implemented, depending on what the routines do and whether they relate to existing functionality exposed by existing controllers in your service. For example, if your service is exposing OData endpoints for Orders, and your Traditional Bridge routines also expose functionality related to Orders, you may choose to expose them via custom endpoint methods in your existing OrdersController class.

But it may be that your Traditional Bridge functions are not related to other controllers in your service. In that case, it probably makes sense to simply add a new custom controller class. This is what is created when you use xfServerPlus migration.

Add the Controller Class

When we used the Harmony Core GUI tool to generate code for our solution after selecting an interface from our Synergy method catalog (SMC), a controller class was generated for the Traditional Bridge routines in the solution. Now we need to add this controller to the Services.Controllers project:

  1. In Solution Explorer, right-click the Services.Controllers project and select Add > Existing Item.

  2. In the Add Existing Item dialog, navigate to the Services.Controllers folder, select BridgeMethodsController.dbl, and click Add.

  3. Open BridgeMethodsController.dbl in the Visual Studio editor and notice the following:

    • The new class inherits from a base class named ControllerBase (actually Microsoft.AspNetCore.Mvc.ControllerBase), which means that it is an MVC controller (previously referred to as a "WebAPI controller").
    • The dependency injection pattern is again being used, this time to get an instance of a class named BridgeMethodsService. Does that sound familiar? It should; it's the class that you wrote in the previous module of this tutorial, the one that exposes "wrapper" methods for your Traditional Bridge routines. Yes, it's all starting to come together!
    • The controller class is decorated with {Route("BridgeMethods")}. This specifies that the endpoints in the controller will be available at the base address of the service.

Endpoint Methods

Similar to the BridgeMethodService class, an endpoint method is needed for each of the three Traditional Bridge routines being exposed.

The GetEnvironment Endpoint Method

  1. Scroll down to the GetEnvironment method and take a few minutes to familiarize yourself with the code:

     {HttpGet}
     {Route("GetEnvironment")}
     {Consumes(MediaTypeNames.Application.Json)}
     {Produces("application/json")}
     ;;; <summary>
     ;;;   Get environment string
     ;;; </summary>
     ;;; <remarks>
     ;;;   The functionality of this endpoint is based on custom code in the underlying application. 
     ;;; </remarks>
     ;;; <response code="200">OK. The operation completed successfully.</response>
     ;;; <response code="400">Bad request. The request was malformed or contained inappropriate content.</response>
     ;;; <response code="500">Internal server error. Report the issue to the API maintainer.</response>
     ;;; <returns>Returned environment string</returns>
     public async method GetEnvironment, @Task<ActionResult<GetEnvironment_Response>>
     proc
         mreturn ok(await _BridgeMethodsService.GetEnvironment())
     endmethod
    

As you can see, this endpoint is available at the URL BridgeMethods/GetEnvironment, and it simply makes a call to the appropriate service wrapper method and returns the returned string value (embedded in a 200 OK response) to the client. In this case, that's all that is needed.

The GetLogicalName Endpoint Method

  1. Now take a look at the GetLogicalName method:

     {HttpPost}
     {Route("GetLogicalName")}
     {Consumes(MediaTypeNames.Application.Json)}
     {Produces("application/json")}
     ;;; <summary>
     ;;;   Get a logical names value
     ;;; </summary>
     ;;; <remarks>
     ;;;   The functionality of this endpoint is based on custom code in the underlying application. 
     ;;; </remarks>
     ;;; <response code="200">OK. The operation completed successfully.</response>
     ;;; <response code="400">Bad request. The request was malformed or contained inappropriate content.</response>
     ;;; <response code="500">Internal server error. Report the issue to the API maintainer.</response>
     ;;; <returns></returns>
     public async method GetLogicalName, @Task<ActionResult<GetLogicalName_Response>>
         {FromBody}
         required in aRequest, @GetLogicalName_Request
     proc
         if (!ModelState.IsValid)
             mreturn BadRequest()
         mreturn ok(await _BridgeMethodsService.GetLogicalName(aRequest))
     endmethod
    
    

This endpoint method is similar to the endpoint for GetEnvironment. The URL route is different, of course, and there is now a parameter decorated with {FromBody}. This means that the body of each BridgeMethods/GetLogicalName request to the service must include the parameter for the method, like this:

 {
     "aLogicalName":"DBLDIR"
 }

The AddTwoNumbers Endpoint Method

  1. Now take a look at the AddTwoNumbers method:

     {HttpPost}
     {Route("AddTwoNumbers")}
     {Consumes(MediaTypeNames.Application.Json)}
     {Produces("application/json")}
     ;;; <summary>
     ;;;   Add two numbers
     ;;; </summary>
     ;;; <remarks>
     ;;;   The functionality of this endpoint is based on custom code in the underlying application. 
     ;;; </remarks>
     ;;; <response code="200">OK. The operation completed successfully.</response>
     ;;; <response code="400">Bad request. The request was malformed or contained inappropriate content.</response>
     ;;; <response code="500">Internal server error. Report the issue to the API maintainer.</response>
     ;;; <returns></returns>
     public async method AddTwoNumbers, @Task<ActionResult<AddTwoNumbers_Response>>
         {FromBody}
         required in aRequest, @AddTwoNumbers_Request
     proc
         if (!ModelState.IsValid)
             mreturn BadRequest()
         mreturn ok(await _BridgeMethodsService.AddTwoNumbers(aRequest))
     endmethod
    

    This endpoint is also similar, but there is a different URL route, and this time there are two parameters, along with the {FromBody} attribute. The body of a request to BridgeMethods/AddTwoNumbers should look like the following, where 1.1 and 2.2 are the numbers that will be added together:

     {
         "number1":1.1,
         "number2":2.2
     }
    

Add Request and Response Model Classes

When we generated code after adding the BridgeMethods interface, a BridgeMethodsServiceModels.dbl file was created in the Services.Models folder in the TraditionalBridge project. This file has request and response model classes, which are classes that define the shape of requests and responses sent to and from the Traditional Bridge routines.

  1. In Solution Explorer, right-click the Models.Services project and select Add > Existing Item from the context menu.

  2. In the Add Existing Item dialog, navigate to the Services.Models folder, select the BridgeMethodsServiceModels.dbl file, and click Add.

  3. Open the file in the Visual Studio editor and scroll down to the GetEnvironment_Response model class. For the GetEnvironment routine, we only need a response class because the routine has no input, just a return value:

    ;;--------------------------------------------------------------------------------
    ;; GetEnvironment
    ;; This method has no in parameters
    
    {JsonObject(MemberSerialization.OptIn)}
    ;;; <summary>
    ;;; Represents OUT parameters and return value for method BridgeMethods.GetEnvironment.
    ;;; </summary>
    public class GetEnvironment_Response
    
        {JsonProperty}
        ;;; <summary>
        ;;; Return value
        ;;; </summary>
        public ReturnValue, string
    
    endclass
    
    

Notice that the class has a JsonObject attribute, and the field for the return value has a JsonProperty attribute.

  1. Next, take a look at the code for GetLogicalName. In this case, there are two model classes: one for the return value and one for the input parameter. The model class for the return value is basically identical to the class we just examined for GetEnvironment. However, because GetLogicalName also takes an input parameter, it has a second class:

    {JsonObject(MemberSerialization.OptIn)}
    ;;; <summary>
    ;;; Represents IN parameters for method BridgeMethods.GetLogicalName.
    ;;; </summary>
    public class GetLogicalName_Request
    
        {JsonProperty}
        {Required(ErrorMessage="aLogicalName is required")}
        ;;; <summary>
        ;;; Parameter 1 (REQUIRED IN string)
        ;;; Logical name
        ;;; </summary>
        public aLogicalName, string
    
    endclass
    

Because the input parameter for GetLogicalName is required, the field has a Required attribute that specifies an error message.

  1. Finally, scroll to the classes for AddTwoNumbers. The response class is similar to what we've seen in the other response model classes (though the result field is a decimal). And the request model class is also similar to what we've already seen, except that it has two input fields, both of which are decimal. Here is the request model class:

    {JsonObject(MemberSerialization.OptIn)}
    ;;; <summary>
    ;;; Represents IN parameters for method BridgeMethods.AddTwoNumbers.
    ;;; </summary>
    public class AddTwoNumbers_Request
    
        {JsonProperty}
        {Required(ErrorMessage="number1 is required")}
        ;;; <summary>
        ;;; Parameter 1 (REQUIRED IN d28.10)
        ;;; First number
        ;;; </summary>
        public number1, decimal
    
        {JsonProperty}
        {Required(ErrorMessage="number2 is required")}
        ;;; <summary>
        ;;; Parameter 2 (REQUIRED IN d28.2)
        ;;; Second number
        ;;; </summary>
        public number2, decimal
    
    endclass
    

Build the Code

Before moving on, make sure the project builds:

  1. Right-click on the Services.Controllers project and select Build.

  2. Check the Output window and verify that the build was successful.

    1>------ Build started: Project: Services.Controllers, Configuration: Any CPU ------
    ========== Build: 1 succeeded, 0 failed, 2 up-to-date, 0 skipped ==========
    

Next topic: Configuring the Traditional Bridge Environment


Clone this wiki locally