Contents
1. Introduction
When you are creating the APIs, you should need to handle many messages and return them to the client, for example, if there is an error or can’t find the data, you need to return the corresponding messages, you can hardcode these messages in the API functions, but just thinking about if your API in the UAT and maybe users want to change the messages again and again, and you will need to find these messages in codes and re-publish them again and again.
If we put all of the messages in the XML files and can be updated in runtime, then we don’t need to change and re-publish the source code, that’s would be great, right?
Ok, let’s start!
2. Define the XML file
As we want to put the messages into XML file, we need to define the XML format, we can base on the XML attribute to get the node’s value, so the format as below
<?xml version="1.0" encoding="utf-8" ?>
<Messages>
<Item id="NoDataFound">There is no data found!</Item>
<Item id="InvalidDateFormat">The date value is invalid!</Item>
</Messages>
We can split the XML file based on each function, this can be easier to manage and you can find the message quickly, for example:
Common.xml
User.xml
3. May the key of messages
There is an id
attribute in our XML file, we need to get the message based on this id
, but I don’t want to hardcode it so we need to create mapping keys below
public static class MsgKey
{
public static class Common
{
public const string NoDataFound = "NoDataFound";
public const string InvalidDateFormat = "InvalidDateFormat";
}
public static class User
{
public const string NoUserFound = "NoUserFound";
public const string UserExist = "UserExist";
public const string UserInvalid = "UserInvalid";
}
}
4. Create the message helper
We need to handle the message from different XML files, so we need to load all files in runtime
//load specify XML files based on the function or section
List<string> xmlFiles = files.Split(',').ToList();
foreach (var file in xmlFiles)
{
//handle the XML file one by one
LoadMessages(file);
}
Handle the XML file
private void LoadMessages(string xmlFile)
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), $"Messages\\{xmlFile}.xml");
var xdoc = XDocument.Load(filePath);
var items = xdoc.Descendants("Item")
.Select(item => new
{
Id = item.Attribute("id").Value,
Value = item.Value
});
foreach (var msg in items)
{
var id = msg.Id;
var message = msg.Value;
if (id != null && !Msg.ContainsKey(id))
{
Msg[id] = message;
}
}
}
the completed codes below
using System.Xml.Linq;
public class MessageHelper
{
public MessageHelper() { }
public Dictionary<string, string> Msg { get; private set; }
public void Init(string files)
{
Msg = new Dictionary<string, string>();
List<string> xmlFiles = files.Split(',').ToList();
foreach (var file in xmlFiles)
{
LoadMessages(file);
}
}
private void LoadMessages(string xmlFile)
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), $"Messages\\{xmlFile}.xml");
var xdoc = XDocument.Load(filePath);
var items = xdoc.Descendants("Item")
.Select(item => new
{
Id = item.Attribute("id").Value,
Value = item.Value
});
foreach (var msg in items)
{
var id = msg.Id;
var message = msg.Value;
if (id != null && !Msg.ContainsKey(id))
{
Msg[id] = message;
}
}
}
}
5. Usage
1) Register the message helper in program.cs
builder.Services.AddScoped<MessageHelper>();
2) Create a testing API controller MessageTesterController
, inject the message helper and pass the XML file name
protected readonly ILogger<MessageTesterController> _logger;
private MessageHelper _messageHelper;
public MessageTesterController(ILogger<MessageTesterController> logger, MessageHelper messageHelper)
{
this._logger = logger;
_messageHelper = messageHelper;
//pass the XML file name in Message folder
_messageHelper.Init("Common,User");
}
Use the key to get the message:
var noDataFoundMsg = _messageHelper.Msg[MsgKey.Common.NoDataFound];
var userInvalidMsg = _messageHelper.Msg[MsgKey.User.UserInvalid];
The completed codes below:
namespace MyDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class MessageTesterController : ControllerBase
{
protected readonly ILogger<MessageTesterController> _logger;
private MessageHelper _messageHelper;
public MessageTesterController(ILogger<MessageTesterController> logger, MessageHelper messageHelper)
{
this._logger = logger;
_messageHelper = messageHelper;
_messageHelper.Init("Common,User");
}
/// <summary>
/// Get the dynamic messages
/// </summary>
/// <returns></returns>
[HttpGet("get-message")]
public async Task<ActionResult<Object>> GetMessage()
{
var apiResult = new ApiResult<Object>();
try
{
var noDataFoundMsg = _messageHelper.Msg[MsgKey.Common.NoDataFound];
var userInvalidMsg = _messageHelper.Msg[MsgKey.User.UserInvalid];
_logger.LogDebug("noDataFoundMsg ================ {0}", noDataFoundMsg);
_logger.LogDebug("userInvalidMsg ================ {0}", userInvalidMsg);
var returnObj = new
{
noDataFound = noDataFoundMsg,
userInvalid = userInvalidMsg
};
apiResult.Data = returnObj;
return Ok(apiResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "MessageTester Exception");
apiResult.Success = false;
apiResult.Message = ex.Message;
return StatusCode(500, apiResult);
}
}
}
}
And you can find the result in the log file:
6. Conclusion
Put the messages in XML files can be easy to manage, and you can update the messages in runtime, this can save you time to re-build and publish your project again and again, also, you can handle the internationalization with this approach, please feel free to let me know if you have any other ideas 🙂
And you can find the completed source code and demo project on my github.