I love the eBiz Soa. It the best thing Aptify’s done in the last… well many… many years (as of publishing this article). But like everything with Aptify where theirs amazing power, there’s also… large misses?
Lol, anyways. Lets talk about my Single Response, Base, Data Query, Post Content and Service Data Object components for eBiz Soa. These components should help in standardizing you endpoint components and make them easier to create/manage and release.
Single Response Object
While, yes, eBiz Soa has the Unity library that allows you to Dependency Inject custom exception handlers… Sorry Aptify, it’s a pain, there’s no full documentation on it and I’ve never got it to work the way I wanted. Note: I didn’t put a lot of time into it either so, ya.
The single response object has made my life much easier and I use it for not only my eBiz Endpoints but for a bunch of my personal projects as well.
The way it works is every endpoint process flow outputs the same base object and a Is Success result code. This way you have 100% control over the data and/or error details being returned to the consumer of the endpoint.
namespace SimplifyAptify.AptifyConfigs.Soa.EndpointComponents.Models
{
public class EndpointComponentResponse<T> where T : class
{
public bool IsSuccess { get; set; } = true;
public T Data { get; set; } = null;
public string Message { get; set; } = string.Empty;
}
public class EndpointComponentResponse
{
public bool IsSuccess { get; set; } = true;
public object Data { get; set; } = null;
public string Message { get; set; } = string.Empty;
}
}
The EndpointComponentResponse
is pretty straight forward.
- IsSuccess - Is the response a success or failure.
- Data - The actual data the endpoint is returning.
- Message - Used for the error message or additional content like maybe you use it for status codes or something.
You might be wondering why I have a generic and a non-generic.
I’ll probably get yelled at by better devs than myself, but, as most of the time you’ll effectively be creating EndpointComponentResponse
for just setting it to the property bag, you won’t really need to set the actual type, unless for serialization reasons.
I didn’t what to constantly type: new EndpointComponentResponse<object>()
every time, I just wanted to do: new EndpointComponentResponse()
.
Lol.
Keep an open mind too, you have Data
that can be anything and Message
as a string.
You can mix and match these in anyway you need to meet the needs of your endpoint.
Base Component Code
NOTE: I am using the PerByte.Rexies library, sorry to those not using it. I tried taking it out but there too many little helper methods that make everything just easier :P.
This abstract class is what all your endpoint components will inherit from.
using Aptify.Framework.DataServices;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using PB.Rexies.AptifyBits;
using PB.Rexies.AptifyBits.ProcessPipeline;
using SimplifyAptify.AptifyConfigs.Soa.EndpointComponents.Models;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
namespace SimplifyAptify.AptifyConfigs.Soa.EndpointComponents
{
public abstract class BaseEndpointComponent : PB.Rexies.AptifyBits.ProcessPipeline.ProcessComponentBase
{
#region Properties
public int? AuthenticatedPrincipalRecordId
{
get => Properties.GetInt32("AuthenticatedPrincipalRecordId");
set => Properties["AuthenticatedPrincipalRecordId"] = value;
}
public const string EndpointComponentResponsePropertyName = "EndpointComponentResponse";
public static string OutputEndpointComponentResponsePropertyName => $"output{EndpointComponentResponsePropertyName}";
#endregion
#region Response Message
public HttpResponseMessage EndpointComponentResponseMessage
{
get {
var response = Properties.GetProperty(OutputEndpointComponentResponsePropertyName);
if (response is HttpResponseMessage message)
{
return message;
}
return null;
}
}
public HttpResponseMessage EndpointComponentResponseSet(EndpointComponentResponse output)
{
if (output == null)
{
output = new EndpointComponentResponse() { IsSuccess = false, Message = "An unknown exception has occurred." };
}
var outputJson = JsonConvert.SerializeObject(output);
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(outputJson, Encoding.UTF8, "application/json")
};
Properties.SetProperty(EndpointComponentResponsePropertyName, output);
EndpointComponentResponseSet(response);
return response;
}
public void EndpointComponentResponseSet(HttpResponseMessage responseMessage)
{
Properties.SetProperty(OutputEndpointComponentResponsePropertyName, responseMessage);
}
#endregion
protected void EnsureAuthenticated()
{
if (
AuthenticatedPrincipalRecordId == null
|| AuthenticatedPrincipalRecordId <= 0
|| (DataAction.ExecuteScalarBooleanParameterized(
"spEndpointComponent_IsAnAnonymousPrincipleRecord_sa",
System.Data.CommandType.StoredProcedure,
DataAction.GetDataParameter(
"@AuthenticatedPrincipalRecordId",
System.Data.SqlDbType.Int,
AuthenticatedPrincipalRecordId
),
DataAction.GetDataParameter(
"@ServiceApplicationName",
System.Data.SqlDbType.NVarChar,
DataAction.UserCredentials.Application
)
) ?? true
))
{
throw new UnauthorizedAccessException("Unauthorized");
}
}
protected override string OnHandleException(Exception ex)
{
// Log Error
Logger.LogError(ex, "{Class}.OnHandleException", GetType().Name);
var output = new EndpointComponentResponse
{
IsSuccess = false,
Message = ex.Message
};
EndpointComponentResponseSet(output);
// eBiz Soa Endpoints need to always return Success so that Aptify doesn't
// try and take over and do it's weirdness
return SuccessResult;
}
}
}
Authenticated User & Allow Anonymous
The EnsureAuthenticated
method is how you’ll enforced authenticated user only.
/*
NAME: spEndpointComponent_IsAnAnonymousPrincipleRecord_sa
DESC: Returns True/False if the given Principle Record Id the Anonymous record for the given Service Application or a Anonymous record for any if no Service Application provided.
TYPE: Stored Procedure
GRANT: GRANT EXECUTE ON spEndpointComponent_IsAnAnonymousPrincipleRecord_sa TO [eBizSoaUser/Group]
*/
CREATE PROCEDURE spEndpointComponent_IsAnAnonymousPrincipleRecord_sa (
@AuthenticatedPrincipalRecordId INT,
@ServiceApplicationName NVARCHAR(50) = NULL
) AS BEGIN
SELECT CAST(ISNULL((
SELECT TOP 1 1
FROM ServiceApplication sa
WHERE sa.AnonymousPerson = @AuthenticatedPrincipalRecordId
AND (RTRIM(ISNULL(@ServiceApplicationName, '')) = '' OR sa.Name = @ServiceApplicationName)
), 0) AS BIT)
END
OnHandleException
With the PB.Rexies.AptifyBits.ProcessPipeline.ProcessComponentBase, it wraps the root ProcessComponentBase Run method within a Try/Catch. This way all exception are captured and logged. But it’s virtual so we can override it for more context based error handling :D.
EndpointComponentResponseSet
The magic output property (you’ll see in part 2) in the endpoints json, allows you to output a HttpResponseMessage
object.
Couple helper methods to make it simpler to set the output.
You’ll also notice we set two properties: EndpointComponentResponse, outputEndpointComponentResponse.
EndpointComponentResponse
We set a property for your raw EndpointComponentResponse
.
This way if your Process Flow needs to execute additional components that might need to interact with it.
outputEndpointComponentResponse
The HttpResponseMessage
for your EndpointComponentResponse
.
eBiz Soa gets the property to return by looking for the property that starts with output.
Part 2 - Data Query Component
Next lets checkout the very cool Data Query Component.
Aptify Clients
Continue the conversation on the Community Forums.