The integration of Generative AI (GenAI) is rapidly moving from a niche experiment to a core business necessity. For developers working with robust platforms like Optimizely CMS 12 and Commerce 14, leveraging AI—such as OpenAI or Azure OpenAI services—can unlock massive efficiencies in content creation, personalization, and product catalog management.
This article provides a step-by-step guide on how to cleanly integrate AI services into your existing .NET 8 application structure, ensuring scalability and maintainability, specifically within the context of an Optimizely project.
1. Configuring the .NET 8 AI Service Foundation
Security and configuration are paramount when dealing with external API keys. We will use .NET 8's standard configuration system and the IHttpClientFactory pattern for robust API communication.
1.1 Configuration Setup
Define your API settings in appsettings.json. We recommend using environment variables or Azure Key Vault for production secrets, but this structure defines the binding target:
{
"OpenAISettings": {
"ApiKey": "sk-YOUR_SECRET_API_KEY",
"ModelName": "gpt-4o-mini",
"BaseUrl": "https://api.openai.com/v1/chat/completions"
}
}
Create a corresponding settings class in MyProject.Model/Settings/OpenAISettings.cs:
namespace MyProject.Model.Settings
{
public class OpenAISettings
{
public const string Position = "OpenAISettings";
public string ApiKey { get; set; } = string.Empty;
public string ModelName { get; set; } = string.Empty;
public string BaseUrl { get; set; } = string.Empty;
}
}
1.2 Registering Services in Program.cs
We register the settings and configure a named HttpClient to handle the necessary Authorization headers using the configuration values.
// File: MyProject.Web/Program.cs
// ... (Existing Optimizely registrations)
builder.Services.Configure<OpenAISettings>(
builder.Configuration.GetSection(OpenAISettings.Position));
// Register IHttpClientFactory for OpenAI
builder.Services.AddHttpClient("OpenAI", (serviceProvider, client) =>
{
var settings = serviceProvider.GetRequiredService<IOptions<OpenAISettings>>().Value;
client.BaseAddress = new Uri(settings.BaseUrl.Substring(0, settings.BaseUrl.LastIndexOf('/')));
// Set required API key header
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.ApiKey);
});
// Register the AI Service
builder.Services.AddSingleton<IAIService, OpenAIService>();
var app = builder.Build();
// ...
2. Building the Generative AI Service Layer
The core logic resides in a dedicated service that manages the request and response serialization. We will use System.Text.Json for modern JSON handling.
2.1 Defining the AI Service Contract
Define the interface for dependency injection (MyProject.Model/Services/IAIService.cs):
namespace MyProject.Model.Services
{
public interface IAIService
{
Task<string> GenerateMarketingContentAsync(string inputTopic, int wordCount);
}
}
2.2 Implementing the OpenAI Service
This service handles the construction of the standardized OpenAI chat request payload.
using System.Text.Json;
using Microsoft.Extensions.Options;
using MyProject.Model.Settings;
namespace MyProject.Model.Services
{
public class OpenAIService : IAIService
{
private readonly HttpClient _httpClient;
private readonly OpenAISettings _settings;
public OpenAIService(IHttpClientFactory httpClientFactory, IOptions<OpenAISettings> options)
{
_httpClient = httpClientFactory.CreateClient("OpenAI");
_settings = options.Value;
}
public async Task<string> GenerateMarketingContentAsync(string inputTopic, int wordCount)
{
var systemPrompt = $@"You are an expert marketing copywriter for an e-commerce platform.
Generate a concise, SEO-friendly marketing description, exactly {wordCount} words long.
The output must only contain the generated text, nothing else.";
var requestPayload = new
{
model = _settings.ModelName,
messages = new[]
{
new { role = "system", content = systemPrompt },
new { role = "user", content = inputTopic }
},
temperature = 0.7
};
var jsonContent = JsonSerializer.Serialize(requestPayload);
var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("chat/completions", content);
if (!response.IsSuccessStatusCode)
{
// Log detailed error response here
throw new HttpRequestException($"OpenAI API call failed: {response.StatusCode}");
}
var responseJson = await response.Content.ReadAsStringAsync();
// Simple deserialization structure (assuming a direct text response)
using (JsonDocument document = JsonDocument.Parse(responseJson))
{
var choice = document.RootElement
.GetProperty("choices")[0]
.GetProperty("message")
.GetProperty("content")
.GetString();
return choice?.Trim() ?? "Failed to generate content.";
}
}
}
}
3. Integrating AI into Optimizely Editors
Once the service is built, Optimizely developers can inject IAIService directly into Controllers, Initialization Modules, or custom admin tools used for editing Content Blocks or Commerce Catalog items.
For example, using it within an existing Content Controller to provide a suggestion based on the page name:
// File: MyProject.Web/Controllers/StandardPageController.cs (Conceptual Example)
public class StandardPageController : PageControllerBase<StandardPage>
{
private readonly IAIService _aiService;
public StandardPageController(IAIService aiService)
{
_aiService = aiService;
}
[HttpGet]
public async Task<ActionResult> Index(StandardPage currentPage)
{
// Example usage: Generating a content summary
if (currentPage.ContentSummary == null)
{
var topic = $"A marketing summary for the page titled '{currentPage.Name}' which focuses on {currentPage.MainBody}";
var generatedSummary = await _aiService.GenerateMarketingContentAsync(topic, 30);
// Note: In a real Optimizely scenario, this should be done in an Edit Mode/Admin action,
// not during front-end rendering. This illustrates service consumption.
}
var model = new StandardPageModel(currentPage);
return View(model);
}
}
4. Troubleshooting Common Integration Issues
Connection or Authorization Errors (401/403)
- Cause: The API Key in
appsettings.jsonis incorrect, expired, or the Bearer token header is malformed. - Solution: Verify the
IHttpClientFactorysetup inProgram.csis correctly reading theApiKeyfromIOptions<OpenAISettings>and setting theAuthorizationheader using"Bearer", settings.ApiKey. Check for trailing whitespace in the key.
JSON Serialization Issues
- Cause: The structure of the request payload sent to the AI service does not match the required schema (e.g., missing
modelor incorrect structure formessages). - Solution: Ensure your anonymous object structure (or POCOs, if used) perfectly matches the OpenAI API documentation, especially the nested array structure for
messages.