← Back to list

Development of JSON-based Custom Service for Microsoft Dynamics 365 F&O

In this article we’ll go over on how to develop an integration between Microsoft Dynamics 365 F&O and third-party services using JSON-based…

Sami Ullah Khalid · 2024-10-01 13:19 · 51 claps · 5.8 min read
#xpp #custom-service #dynamics-365-finance #integration #development
Open on Medium ↗

Development of JSON-based Custom Service for Microsoft Dynamics 365 F&O

In this article we’ll go over on how to develop an integration between Microsoft Dynamics 365 F&O and third-party services using JSON-based custom service. And test it using Postman.

Prerequisites:

  1. Admin access to a MS D365 F&O development environment.
  2. Admin access to MS D365 F&O application on development environment.
  3. Review and complete the process from the article Add New External Application in Microsoft Dynamics 365 F&O.
  4. Postman (Desktop Or Online).

Process:

  1. Setup Visual Studio for Microsoft Dynamics 365 F&O Development.
  2. Development of X++ Custom Service.
  3. API Testing using Postman for Microsoft Dynamics 365 F&O JSON-based Custom Service.

Step 1: Setup Visual Studio for Microsoft Dynamics 365 F&O Development.

Review and complete the process from the article: Setup Visual Studio for Microsoft Dynamics 365 F&O Development.

Step 2: Development of X++ Custom Service.

  1. Create a Class with the Name CSRequest”.

In the Solution Explorer, Right click on the Project.

Select Add > New item.

Under FinanceOperations > Dynamics 365 Items > Code > Class.

Enter the Name, then click on Add.

  1. Copy and then paste the following code in the Class CSRequest”.

After paste. Click on Save. Or press Ctrl + S.

[DataContractAttribute]
class CSRequest
{
    // Variables used in request body
    private str dataAreaId;
    private str data;

    // For storing dataAreaId value from request body
    [DataMember("dataAreaId")]
    public str parmDataAreaId(str _value = dataAreaId)
    {
        if (!prmIsDefault(_value))
        {
            dataAreaId = _value;
        }

        return dataAreaId;
    }

    // For storing Data value from request body
    [DataMember("Data")]
    public str parmData(str _value = data)
    {
        if (!prmIsDefault(_value))
        {
            data = _value;
        }

        return data;
    }

}

  1. Create a Class with the Name CSResponse”. (Refer to Step 2 point 1)

Copy and then paste the following code in the Class CSResponse”.

After paste. Click on Save. Or press Ctrl + S.

[DataContractAttribute]
class CSResponse
{
    // Variable used in response body
    private boolean     success;
    private str         errorMessage;
    private str         data;

    // For storing Success status value for response body
    [DataMember("Success")]
    public Boolean parmSuccess(Boolean _value = success)
    {
        if (!prmIsDefault(_value))
        {
            success = _value;
        }

        return success;
    }

    // For storing ErrorMessage value for response body
    [DataMember("ErrorMessage")]
    public str parmErrorMessage(str _value = errorMessage)
    {
        if (!prmIsDefault(_value))
        {
            errorMessage = _value;
        }

        return errorMessage;
    }

    // For storing Data value for response body
    [DataMember("Data")]
    public str parmData(str _value = data)
    {
        if (!prmIsDefault(_value))
        {
            data = _value;
        }

        return data;
    }

}

  1. Create a Class with the Name CSEngine”. (Refer to Step 2 point 1)

Copy and then paste the following code in the Class CSEngine”.

After paste. Click on Save. Or press Ctrl + S.

public class CSEngine
{
    // Global variables used for error handling
    str errorMsg;
    int infologStartLine;

    // Entry point for the custom service method getData
    public CSResponse getData(CSRequest _request)
    {
        // Validating request body data
        str isValidMsg = this.validateRequestGetData(_request);

        // Evaluating request is valid
        if (isValidMsg == 'true')
        {
            // Processing the request
            return this.processRequestGetData(_request);
        }
        else
        {
            // Returning response for invalid data in request body
            return this.createResponse(false, isValidMsg, '');
        }
    }

    // Methond to generate response body
    private CSResponse createResponse(boolean _success, str _errorMessage, str _data)
    {
        var response = new CSResponse();

        response.parmSuccess(_success);
        response.parmErrorMessage(_errorMessage);
        response.parmData(_data);

        return response;
    }

    // Method for obtaining error message
    private str getInfologStr(int _infologStartLine)
    {
        SysInfologEnumerator    enumerator;
        SysInfologMessageStruct msgStruct;
        str                     error;
        container               infologData;

        int infologCurrentLine = infologLine();

        if (infologCurrentLine > _infologStartLine)
        {
            infologData = infolog.copy(_infologStartLine + 1, infologCurrentLine);
        }

        if (infologData)
        {
            enumerator = SysInfologEnumerator::newData(infologData);

            while (enumerator.moveNext())
            {
                msgStruct = new SysInfologMessageStruct(enumerator.currentMessage());
                error = strfmt("@SYS324543", error, msgStruct.message());
            }
        }
        return error;
    }

    // Method for adding error message to the global variable
    private void addErrorMsg(str _errorMsg)
    {
        errorMsg += _errorMsg;
    }

    // Method for validating the request body data
    private str validateRequestGetData(CSRequest _request)
    {
        str         isValid;
        DataAreaId  dataAreaId;
        str         data;

        dataAreaId  = _request.parmDataAreaId();
        data        = _request.parmData();
        isValid     = 'true';

        if (!CompanyInfo::findDataArea(dataAreaId))
        {
            return 'dataAreaId does not exist.';
        }
        if (data == '')
        {
            return 'No data in the request body.';
        }

        return isValid;
    }

    // Method for executing custom logic for the request getData
    private CSResponse processRequestGetData(CSRequest _request)
    {
        str responseData;

        responseData    = '';

        // Setting the infolog scope to capture Error message for the current operation only
        infologStartLine = infologLine();

        try
        {
            // For executing company specific logic
            changecompany(_request.parmDataAreaId())
            {
                // For creating the JSON response body
                responseData = this.generateJSONResponseForGetData(_request);
            }
        }
        // Exception handling for CLRError
        catch (Exception::CLRError)
        {
            System.Exception exception = CLRInterop::getLastException();

            return this.createResponse(false, exception.Message, '');
        }
        // Exception handling for Error
        catch (Exception::Error)
        {
            this.addErrorMsg(this.getInfologStr(infologStartLine));

            return this.createResponse(false, errorMsg, '');
        }

        // Success state of the operation getData and returning the response
        return this.createResponse(true, '', responseData);
    }

    // Method for generating the JSON response data for the request getData
    private str generateJSONResponseForGetData(CSRequest _request)
    {
        str                             jsonString;
        System.IO.StringWriter          stringWriter;
        Newtonsoft.Json.JsonTextWriter  jsonWriter;

        stringWriter    = new System.IO.StringWriter();
        jsonWriter      = new Newtonsoft.Json.JsonTextWriter(stringWriter);

        jsonString         = '';

        jsonWriter.WriteStartObject();

        jsonWriter.WritePropertyName("JSONArray");
        jsonWriter.WriteStartArray();

        for (int i = 0; i < 2; i++)
        {
            jsonWriter.WriteStartObject();
            jsonWriter.WritePropertyName("dataAreaId");
            jsonWriter.WriteValue(curExt());
            jsonWriter.WritePropertyName("Data");
            jsonWriter.WriteValue(_request.parmData());
            jsonWriter.WriteEndObject();
        }

        jsonWriter.WriteEndArray();
        jsonWriter.WriteEndObject();

        jsonString = stringWriter.ToString();
        jsonString = FormJsonSerializer::serializePrimitive(jsonString);

        return jsonString;
    }

}

  1. Create a Service with the Name CustomService”.

Right click on the Project.

Select Add > New item.

Under FinanceOperations > Dynamics 365 Items > Services > Service.

Enter the Name, then click on Add.

  1. Adding the service Class.

Open the Service CustomService”.

In the designer, select the “CustomService”.

Right click on it, to open the Properties. Or Press Alt + Enter.

In the property Class, enter/select “CSEngine”.

In the property External Name, enter “CustomService”.

Right click on Service Operations, and select New Service Operation.

Select the new service operation ServiceOperation1.

Right click on it, and select the Properties. Or Press Alt + Enter.

  1. Adding the entry point for the service operation.

In the property Method, enter/select “getData”.

In the property Name, enter “getData”.

Click on Save. Or press Ctrl + S.

  1. Create a Service Group with the Name CSGroup”.

Right click on the Project.

Select Add > New item.

Under FinanceOperations > Dynamics 365 Items > Services > Service Group.

Enter the Name, then click on Add.

  1. Adding the Service “CustomService” to the Service Group.

Open the Service Group CSGroup”.

In the designer, select the “CSGroup”.

Right click on it, and select New Service.

Right click on the newly created Service Group Service, and select to open the Properties. Or Press Alt + Enter.

In the property Name, enter/select “CustomService”.

In the property Service, enter “CustomService”.

Click on Save. Or press Ctrl + S.

  1. Build the Project.

In the Solution Explorer, right click on the Project, and select Build.

After Build completes successfully, Synchronize the Project with database.

Step 3: API Testing using Postman for Microsoft Dynamics 365 F&O JSON-based Custom Service.

Review and complete the process from the article: API Testing Using Postman for Microsoft Dynamics 365 F&O JSON-based Custom Service.

Conclusion:

We successfully developed a new JSON-based custom service and tested it using Postman.

References:

Microsoft article: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/custom-services


메타데이터
post_id
f2c8ff9b1099
slug
development-of-json-based-custom-service-for-microsoft-dynamics-365-f-o-f2c8ff9b1099
url
https://medium.com/@samikhalid22/development-of-json-based-custom-service-for-microsoft-dynamics-365-f-o-f2c8ff9b1099
canonical_url
https://medium.com/@samikhalid22/development-of-json-based-custom-service-for-microsoft-dynamics-365-f-o-f2c8ff9b1099
author_url
https://medium.com/@samikhalid22
status
ok
fetched_at
2026-07-22 16:12:32