- Introduction
- What Are Rich Push Notifications?
- Why ASP.NET Core, OneSignal, and Razor?
- Goals of This Guide
- Business Case
- Scenario: Multi-App E-Commerce Ecosystem
- Objectives and Requirements
- Technical Overview
- Android Push Notification Architecture
- OneSignal’s Role in Android Notifications
- Handling Bangla and English Text
- Role of Razor UI
- Prerequisites
- Tools and Accounts Needed
- Android App Setup
- Step-by-Step Implementation
- Setting Up the ASP.NET Core MVC Project
- Configuring OneSignal for Multiple Android Apps
- Designing Notification Models
- Building the Notification Service
- Creating the API Controller
- Designing the Razor UI
- Implementing Error Handling
- Adding Logging and Monitoring
- Testing the Solution
- Complete Code Example
- Project Structure
- Configuration Files
- Models
- Services
- Controllers
- Razor Views
- Program Setup
- Advanced Scenarios
- Localization for Bangla and English
- Dynamic Content Personalization
- Handling Images and Icons
- High-Volume Notification Campaigns
- Rate Limiting and Throttling
- A/B Testing
- Error Handling Mechanisms
- Handling API Failures
- Managing Rate Limits
- Dealing with Invalid Devices
- Retry Policies with Polly
- UI Error Feedback
- Pros and Cons
- Advantages of OneSignal with ASP.NET Core and Razor
- Limitations and Challenges
- Alternatives to OneSignal
- Firebase Cloud Messaging (FCM)
- Amazon SNS
- Custom Push Server
- Comparison with OneSignal
- Feature Comparison
- Pricing Comparison
- Scalability and Performance
- Best Practices
- Security Considerations
- Performance Optimization
- Localization and Unicode Support
- User Consent and Compliance
- UI Usability
- Conclusion
- References
- Multilingual Text: Support for Bangla (Bengali) and English.
- Images: Large images (e.g., 1440x720px, 2:1 ratio) for visual appeal.
- Icons: Small and large icons for branding.
- Action Buttons: Interactive options like “Shop Now.”
- Custom Data: Metadata for deep linking or analytics.
- Scalability: Handles high traffic and large user bases.
- Dependency Injection: Simplifies integration with APIs.
- MVC Pattern: Supports Razor for dynamic UIs.
- Security: Built-in HTTPS and authentication.
- Ease of Use: Simple REST API and Android SDK.
- Rich Notifications: Supports images, icons, and actions.
- Localization: Handles Unicode for Bangla.
- Multi-App Support: Manages multiple apps with one API.
- Free Tier: Unlimited mobile push sends.
- User Accessibility: Allows non-technical users (e.g., marketing teams) to send notifications.
- Dynamic Forms: Simplifies input for text, images, and scheduling.
- Real-Time Feedback: Displays success or error messages.
- Provide a complete C# implementation for sending rich notifications to multiple Android apps.
- Support Bangla and English text, images, and icons.
- Include a Razor-based UI for managing notifications.
- Implement robust error handling for API failures, rate limits, and invalid devices.
- Address advanced scenarios like localization, personalization, and high-volume campaigns.
- Offer best practices for security, performance, compliance, and UI usability.
- Compare OneSignal with alternatives like FCM.
- Serve as a publishable blog post for developers and stakeholders.
- BazaarHub Fashion: Clothing and accessories.
- BazaarHub Electronics: Gadgets and appliances.
- BazaarHub Grocery: Grocery delivery.
- Unified Campaigns: Send identical notifications to all apps.
- Localized Content: Include Bangla (“৫০% ছাড়!”) and English (“50% Off!”).
- Visual Engagement: Use product images and branded icons.
- User Interaction: Add action buttons for deep linking.
- User-Friendly UI: Enable marketing teams to send notifications via a web interface.
- Reliability: Ensure delivery during high-traffic campaigns.
- Compliance: Adhere to user consent and privacy laws.
- Rich Notifications: Bangla/English text, images, and app-specific icons.
- Multi-App Targeting: Send to all apps via one API call or UI form.
- Scheduling: Deliver at optimal times (e.g., 8 PM local time).
- Error Handling: Manage API errors, rate limits, and undeliverable devices.
- Razor UI: Intuitive interface for composing and sending notifications.
- Analytics: Track delivery, open rates, and clicks.
- Scalability: Support 100,000+ users.
- Ramadan Sale: All apps send a notification with a sale banner, Bangla/English text, and a “Shop Now” button.
- Cart Abandonment: Remind users of abandoned items with product images and localized text.
- New Product Launch: Announce a smartphone in the Electronics app, with English fallback.
- Application Server: ASP.NET Core app sends requests to OneSignal’s API.
- OneSignal: Manages subscriptions, formats notifications, and routes to FCM.
- FCM: Delivers notifications to Android devices.
- Android App: Renders notifications with text, images, and icons.
- Register devices for push notifications.
- Handle rich media (images up to 5MB, icons).
- Support Unicode for Bangla (U+0980 to U+09FF).
- Enable action buttons and deep links.
- Provide analytics for tracking.
- Unicode Support: Bangla uses UTF-8, supported by OneSignal and Android.
- Localization: Use contents field for both languages, with device language detection.
- Fallback: Default to English for non-Bangla devices.
- Provide a form to input Bangla/English text, image URLs, and scheduling details.
- Allow selection of target apps and segments.
- Display success/error messages after sending.
- Use Bootstrap for responsive design.
- Integrate with the backend API for seamless operation.
- Development Environment:
- Visual Studio 2022 or VS Code with C# extensions.
- Android Studio for SDK testing.
- OneSignal Account:
- Create apps for Fashion, Electronics, and Grocery.
- Obtain App IDs and REST API Key.
- Firebase Account:
- Generate FCM Server Key and Sender ID.
- NuGet Packages:
- Microsoft.AspNetCore.Mvc.Razor
- Microsoft.Extensions.Http
- System.Text.Json
- Microsoft.Extensions.Logging
- Polly
- Serilog.AspNetCore
- Add OneSignal SDK: In app/build.gradle:gradle
dependencies { implementation 'com.onesignal:OneSignal:[5.0.0, 5.99.99]' } - Initialize OneSignal: In MainActivity.java:java
import com.onesignal.OneSignal; import com.onesignal.OSNotificationOpenResult; import com.onesignal.OneSignalNotificationOpenedHandler; public class MainActivity extends AppCompatActivity { private static final String ONESIGNAL_APP_ID = "your-app-id"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); OneSignal.initWithContext(this, ONESIGNAL_APP_ID); OneSignal.setNotificationOpenedHandler(new NotificationOpenedHandler()); OneSignal.setLanguage("bn"); OneSignal.promptForPushNotifications(); } } class NotificationOpenedHandler implements OneSignalNotificationOpenedHandler { @Override public void notificationOpened(OSNotificationOpenResult result) { String url = result.getNotification().getAdditionalData().optString("url"); if (url != null) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); } } } - Configure FCM:
- Link to Firebase project.
- Add google-services.json to app directory.
- Set Icons: In AndroidManifest.xml:xml
<application> <meta-data android:name="com.onesignal.NotificationLargeIcon" android:resource="@drawable/large_icon" /> <meta-data android:name="com.onesignal.NotificationSmallIcon" android:resource="@drawable/small_icon" /> </application>Place large_icon.png (96x96px) and small_icon.png (24x24px) in res/drawable. - Test Subscription:
- Run the app and verify subscription in OneSignal’s dashboard.
- Create an MVC Project:bash
dotnet new mvc -n BazaarHubNotifications cd BazaarHubNotifications - Install NuGet Packages:bash
dotnet add package Microsoft.Extensions.Http dotnet add package System.Text.Json dotnet add package Microsoft.Extensions.Logging dotnet add package Polly --version 8.0.0 dotnet add package Serilog.AspNetCore --version 8.0.0 - Project Structure:plaintext
BazaarHubNotifications/ ├── Configuration/ │ ├── OneSignalSettings.cs ├── Models/ │ ├── NotificationRequest.cs │ ├── OneSignalNotification.cs ├── Services/ │ ├── INotificationService.cs │ ├── OneSignalNotificationService.cs ├── Controllers/ │ ├── NotificationsController.cs │ ├── HomeController.cs ├── Views/ │ ├── Home/ │ │ ├── Index.cshtml │ ├── Notifications/ │ │ ├── Send.cshtml │ │ ├── Result.cshtml │ ├── Shared/ │ │ ├── _Layout.cshtml ├── wwwroot/ │ ├── css/ │ │ ├── site.css │ ├── js/ │ │ ├── site.js │ ├── lib/ │ │ ├── bootstrap/ ├── Logs/ │ ├── notification.log ├── appsettings.json ├── Program.cs ├── BazaarHubNotifications.csproj - Configure Serilog: In Program.cs:csharp
using Microsoft.AspNetCore.Mvc; using Serilog; using Serilog.Events; using BazaarHubNotifications.Configuration; using BazaarHubNotifications.Services; var builder = WebApplication.CreateBuilder(args); // Configure Serilog Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .WriteTo.Console() .WriteTo.File("Logs/notification.log", rollingInterval: RollingInterval.Day) .CreateLogger(); builder.Host.UseSerilog(); // Add services builder.Services.AddControllersWithViews(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.Configure<OneSignalSettings>(builder.Configuration.GetSection("OneSignal")); builder.Services.AddHttpClient<INotificationService, OneSignalNotificationService>(); var app = builder.Build(); // Configure pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); try { Log.Information("Starting application"); app.Run(); } catch (Exception ex) { Log.Fatal(ex, "Application failed to start"); throw; } finally { Log.CloseAndFlush(); }
- Create OneSignal Apps:
- Create apps for Fashion, Electronics, and Grocery in the OneSignal dashboard.
- Add FCM Server Key and Sender ID from Firebase.
- Enable large image support.
- Store Credentials: In appsettings.json:json
{ "OneSignal": { "Apps": [ { "AppId": "fashion-app-id", "Name": "BazaarHub Fashion" }, { "AppId": "electronics-app-id", "Name": "BazaarHub Electronics" }, { "AppId": "grocery-app-id", "Name": "BazaarHub Grocery" } ], "ApiKey": "your-rest-api-key" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }Create Configuration/OneSignalSettings.cs:csharpnamespace BazaarHubNotifications.Configuration; public class OneSignalSettings { public List<OneSignalApp> Apps { get; set; } = new(); public string ApiKey { get; set; } } public class OneSignalApp { public string AppId { get; set; } public string Name { get; set; } }
- Input Model: Create Models/NotificationRequest.cs:csharp
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace BazaarHubNotifications.Models; public class NotificationRequest { [Required(ErrorMessage = "Bangla title is required")] public string TitleBangla { get; set; } public string TitleEnglish { get; set; } [Required(ErrorMessage = "Bangla message is required")] public string MessageBangla { get; set; } public string MessageEnglish { get; set; } [Url(ErrorMessage = "Invalid image URL")] public string ImageUrl { get; set; } [Url(ErrorMessage = "Invalid large icon URL")] public string LargeIconUrl { get; set; } [Url(ErrorMessage = "Invalid small icon URL")] public string SmallIconUrl { get; set; } public List<string> AppIds { get; set; } = new(); public List<string> UserIds { get; set; } = new(); public List<string> Segments { get; set; } = new(); public DateTime? ScheduleTime { get; set; } public List<NotificationAction> Actions { get; set; } = new(); public Dictionary<string, string> CustomData { get; set; } = new(); } public class NotificationAction { public string Id { get; set; } public string Text { get; set; } public string Url { get; set; } } - OneSignal Payload Model: Create Models/OneSignalNotification.cs:csharp
using System.Collections.Generic; using System.Text.Json.Serialization; namespace BazaarHubNotifications.Models; public class OneSignalNotification { [JsonPropertyName("app_id")] public string AppId { get; set; } [JsonPropertyName("contents")] public Dictionary<string, string> Contents { get; set; } = new(); [JsonPropertyName("headings")] public Dictionary<string, string> Headings { get; set; } = new(); [JsonPropertyName("included_segments")] public List<string> IncludedSegments { get; set; } = new(); [JsonPropertyName("include_external_user_ids")] public List<string> IncludeExternalUserIds { get; set; } = new(); [JsonPropertyName("big_picture")] public string BigPicture { get; set; } [JsonPropertyName("large_icon")] public string LargeIcon { get; set; } [JsonPropertyName("small_icon")] public string SmallIcon { get; set; } [JsonPropertyName("actions")] public List<NotificationAction> Actions { get; set; } = new(); [JsonPropertyName("send_after")] public string SendAfter { get; set; } [JsonPropertyName("data")] public Dictionary<string, string> Data { get; set; } = new(); [JsonPropertyName("idempotency_key")] public string IdempotencyKey { get; set; } }
- Define Interface: Create Services/INotificationService.cs:csharp
using BazaarHubNotifications.Models; using System.Threading.Tasks; namespace BazaarHubNotifications.Services; public interface INotificationService { Task<(bool Success, string Message)> SendNotificationAsync(NotificationRequest request); } - Implement Service: Create Services/OneSignalNotificationService.cs:csharp
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using BazaarHubNotifications.Configuration; using BazaarHubNotifications.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; using Polly; using Polly.Retry; namespace BazaarHubNotifications.Services; public class OneSignalNotificationService : INotificationService { private readonly HttpClient _httpClient; private readonly OneSignalSettings _settings; private readonly ILogger<OneSignalNotificationService> _logger; private readonly AsyncRetryPolicy _retryPolicy; private static readonly SemaphoreSlim _throttle = new SemaphoreSlim(10); public OneSignalNotificationService( HttpClient httpClient, IOptions<OneSignalSettings> settings, ILogger<OneSignalNotificationService> logger) { _httpClient = httpClient; _settings = settings.Value; _logger = logger; _httpClient.BaseAddress = new Uri("https://onesignal.com/api/v1/"); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {_settings.ApiKey}"); _retryPolicy = Policy .Handle<HttpRequestException>() .OrResult<HttpResponseMessage>(r => r.StatusCode == System.Net.HttpStatusCode.TooManyRequests) .WaitAndRetryAsync( retryCount: 3, sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetryAsync: (result, timeSpan, retryAttempt, context) => { _logger.LogWarning( "Retry {RetryAttempt} after {TimeSpan}ms due to {Reason}", retryAttempt, timeSpan.TotalMilliseconds, result.Exception?.Message ?? result.Result.StatusCode.ToString()); return Task.CompletedTask; }); } public async Task<(bool Success, string Message)> SendNotificationAsync(NotificationRequest request) { await _throttle.WaitAsync(); try { if (string.IsNullOrEmpty(request.TitleBangla) || string.IsNullOrEmpty(request.MessageBangla)) { _logger.LogError("Bangla title or message is missing"); return (false, "Bangla title and message are required."); } var targetAppIds = request.AppIds.Any() ? _settings.Apps.Where(a => request.AppIds.Contains(a.AppId)).Select(a => a.AppId).ToList() : _settings.Apps.Select(a => a.AppId).ToList(); if (!targetAppIds.Any()) { _logger.LogError("No valid App IDs specified"); return (false, "No valid apps selected."); } var successes = new List<string>(); var failures = new List<string>(); var errorMessages = new List<string>(); foreach (var appId in targetAppIds) { var notification = new OneSignalNotification { AppId = appId, Contents = new Dictionary<string, string> { { "bn", request.MessageBangla }, { "en", request.MessageEnglish ?? request.MessageBangla } }, Headings = new Dictionary<string, string> { { "bn", request.TitleBangla }, { "en", request.TitleEnglish ?? request.TitleBangla } }, IncludedSegments = request.Segments, IncludeExternalUserIds = request.UserIds, BigPicture = request.ImageUrl, LargeIcon = request.LargeIconUrl, SmallIcon = request.SmallIconUrl, Actions = request.Actions, SendAfter = request.ScheduleTime?.ToString("yyyy-MM-dd HH:mm:ss 'GMT'"), Data = request.CustomData, IdempotencyKey = Guid.NewGuid().ToString() }; var json = JsonSerializer.Serialize(notification, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, IgnoreNullValues = true }); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _retryPolicy.ExecuteAsync(() => _httpClient.PostAsync("notifications", content)); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize<Dictionary<string, object>>(responseContent); if (result.ContainsKey("errors") && result["errors"].ToString().Contains("invalid_player_ids")) { var invalidIds = JsonSerializer.Deserialize<List<string>>(result["invalid_player_ids"].ToString()); _logger.LogWarning("Invalid player IDs for App ID {AppId}: {InvalidIds}", appId, string.Join(", ", invalidIds)); // Queue for cleanup } _logger.LogInformation( "Notification sent successfully to App ID {AppId}. Notification ID: {NotificationId}", appId, result["id"]); successes.Add(_settings.Apps.First(a => a.AppId == appId).Name); } else { var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogError( "Failed to send notification to App ID {AppId}. Status: {StatusCode}, Error: {Error}", appId, response.StatusCode, errorContent); failures.Add(_settings.Apps.First(a => a.AppId == appId).Name); errorMessages.Add($"Failed for {_settings.Apps.First(a => a.AppId == appId).Name}: {errorContent}"); } } if (failures.Any()) { _logger.LogWarning( "Notification failed for {FailureCount} apps: {FailedApps}", failures.Count, string.Join(", ", failures)); return (false, $"Failed to send to some apps: {string.Join("; ", errorMessages)}"); } return (true, $"Notification sent successfully to: {string.Join(", ", successes)}"); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error sending notification: {Message}", ex.Message); return (false, $"Unexpected error: {ex.Message}"); } finally { _throttle.Release(); } } }
using Microsoft.AspNetCore.Mvc;
using BazaarHubNotifications.Models;
using BazaarHubNotifications.Services;
using System.Threading.Tasks;
namespace BazaarHubNotifications.Controllers;
[Route("api/[controller]")]
[ApiController]
public class NotificationsController : ControllerBase
{
private readonly INotificationService _notificationService;
public NotificationsController(INotificationService notificationService)
{
_notificationService = notificationService;
}
[HttpPost("send")]
public async Task<IActionResult> SendNotification([FromBody] NotificationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var (success, message) = await _notificationService.SendNotificationAsync(request);
if (success)
{
return Ok(new { Message = message });
}
return StatusCode(500, new { Message = message });
}
}Home Page: Update Views/Home/Index.cshtml:
cshtml
Send Notification Form: Create Views/Notifications/Send.cshtml:
Result Page: Create Views/Notifications/Result.cshtml:
- Home Controller: Update Controllers/HomeController.cs:csharp
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System.Diagnostics; namespace BazaarHubNotifications.Controllers; public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } public IActionResult Index() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } - Notifications Controller (MVC): Create Controllers/NotificationsController.cs (merge with API controller):csharp
using Microsoft.AspNetCore.Mvc; using BazaarHubNotifications.Models; using BazaarHubNotifications.Services; using System.Threading.Tasks; namespace BazaarHubNotifications.Controllers; public class NotificationsController : Controller { private readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; } [HttpGet] public IActionResult Send() { return View(new NotificationRequest()); } [HttpPost] public async Task<IActionResult> Send(NotificationRequest request) { if (!ModelState.IsValid) { return View(request); } var (success, message) = await _notificationService.SendNotificationAsync(request); ViewBag.Success = success; ViewBag.Message = message; return View("Result"); } [HttpPost("api/notifications/send")] [ApiExplorerSettings(GroupName = "api")] public async Task<IActionResult> SendApi([FromBody] NotificationRequest request) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var (success, message) = await _notificationService.SendNotificationAsync(request); if (success) { return Ok(new { Message = message }); } return StatusCode(500, new { Message = message }); } } - CSS Styling: Update wwwroot/css/site.css:css
body { font-family: Arial, sans-serif; } .form-control { max-width: 500px; } .form-check { margin-bottom: 10px; } .alert { max-width: 600px; margin: 20px auto; } .btn-primary { margin-right: 10px; }
- Validation Errors:
- Use DataAnnotations in NotificationRequest.
- Display errors in the UI via asp-validation-for.
- API Failures:
- Polly retries transient errors (e.g., HTTP 429).
- Log detailed responses.
- Invalid Devices:
- Handle invalid_player_ids in the service.
- Log for cleanup.
- UI Feedback:
- Show success/error messages on the Result page.
- Use AJAX to prevent page reloads.
- Serilog:
- Logs to console and Logs/notification.log.
- Capture API responses and errors.
- Monitoring:
- Check OneSignal’s analytics for delivery and clicks.
- Add a logs endpoint (optional):csharp
[HttpGet("logs")] public IActionResult Logs() { var logs = System.IO.File.ReadAllLines("Logs/notification.log"); return Json(logs); }
using Microsoft.AspNetCore.Mvc;
using Serilog;
using Serilog.Events;
using BazaarHubNotifications.Configuration;
using BazaarHubNotifications.Services;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.WriteTo.Console()
.WriteTo.File("Logs/notification.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog();
// Add services
builder.Services.AddControllersWithViews();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.Configure<OneSignalSettings>(builder.Configuration.GetSection("OneSignal"));
builder.Services.AddHttpClient<INotificationService, OneSignalNotificationService>();
var app = builder.Build();
// Configure pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
try
{
Log.Information("Starting application");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application failed to start");
throw;
}
finally
{
Log.CloseAndFlush();
- Use contents and headings with "bn" and "en" keys.
- Set device language in the Android app:java
OneSignal.setLanguage("bn"); - Fallback to English:csharp
Contents = new Dictionary<string, string> { { "bn", request.MessageBangla }, { "en", request.MessageEnglish ?? request.MessageBangla } };
- Store user data as tags:java
OneSignal.sendTag("username", "Rahim"); - Include in CustomData:csharp
request.CustomData = new Dictionary<string, string> { { "username", "Rahim" }, { "product", "Smartphone" } }; - Parse in the Android app:java
String username = result.getNotification().getAdditionalData().optString("username");
- Images: Use 1440x720px, host on a CDN, validate URLs:csharp
if (!Uri.TryCreate(request.ImageUrl, UriKind.Absolute, out _)) { return (false, "Invalid image URL."); } - Icons: Large (96x96px), small (24x24px), fallback to app resources.
- Batch Processing:csharp
public async Task<(bool, string)> SendToLargeAudienceAsync(NotificationRequest request, List<string> userIds) { const int batchSize = 2000; var successes = new List<string>(); for (int i = 0; i < userIds.Count; i += batchSize) { request.UserIds = userIds.Skip(i).Take(batchSize).ToList(); var (success, message) = await SendNotificationAsync(request); if (success) successes.Add(message); } return successes.Any() ? (true, string.Join("; ", successes)) : (false, "No batches succeeded."); }
- Use _throttle semaphore.
- Polly handles HTTP 429 with exponential backoff.
- Create variants in OneSignal dashboard.
- Assign via API:csharp
notification.VariantName = "variant_a";
- Validation: DataAnnotations and UI feedback.
- API Failures: Polly retries, detailed logging.
- Invalid Devices: Log and queue for cleanup.
- UI Feedback: Success/error messages on Result page.
- Rate Limits: Throttling and retries.
- OneSignal: Easy API, rich features, free tier.
- ASP.NET Core: Scalable, secure, MVC support.
- Razor UI: User-friendly, responsive.
- Dependency: Relies on OneSignal.
- Image Restrictions: 5MB limit, no GIFs.
- Cost: Advanced features require paid plans.
- FCM: Free, but less targeting.
- Amazon SNS: Scalable, complex setup.
- Custom Server: Full control, high effort.
Feature | OneSignal | FCM | SNS |
|---|---|---|---|
Rich Notifications | Yes | Yes | Limited |
Localization | Yes | Manual | Manual |
Multi-App | Yes | Yes | Yes |
Free Tier | Unlimited mobile push | Free | 1M free |
- Security: Store API keys securely, use HTTPS.
- Performance: Batch processing, caching.
- Localization: UTF-8 for Bangla, fallback to English.
- Compliance: Obtain user consent.
- UI Usability: Responsive design, clear feedback.
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam