Update most resources to use urlFromPieces

Only id in event_service and account_service have not been updated due
to the risk of it breaking the username/id. It will require further
testing to verify.

Use urlFromPieces wherever that is needed to insert a variable in the
URI. Don't use urlFromPieces when it is hardcoded values. This allow us
to control all resource URIs that is dynamically added and to sync with
the current recommanded method for `@odata.id`. The goal is to have a
common place to manage the url created from dbus-paths in order to
manage/update it easily when needed.

Tested:
RedfishValidtor Passed for all resource including the sensors with the
fragments.

Change-Id: I95cdfaaee58fc7f21c95f5944e1e5c813b3215f2
Signed-off-by: Willy Tu <wltu@google.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/redfish-core/lib/cable.hpp b/redfish-core/lib/cable.hpp
index 51eca27..0fc36b4 100644
--- a/redfish-core/lib/cable.hpp
+++ b/redfish-core/lib/cable.hpp
@@ -151,7 +151,8 @@
 
                 asyncResp->res.jsonValue["@odata.type"] = "#Cable.v1_0_0.Cable";
                 asyncResp->res.jsonValue["@odata.id"] =
-                    "/redfish/v1/Cables/" + cableId;
+                    crow::utility::urlFromPieces("redfish", "v1", "Cables",
+                                                 cableId);
                 asyncResp->res.jsonValue["Id"] = cableId;
                 asyncResp->res.jsonValue["Name"] = "Cable";
 
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index 5489351..4db6719 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -274,16 +274,21 @@
             asyncResp->res.jsonValue["@odata.type"] =
                 "#Chassis.v1_16_0.Chassis";
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Chassis/" + chassisId;
+                crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                             chassisId);
             asyncResp->res.jsonValue["Name"] = "Chassis Collection";
             asyncResp->res.jsonValue["ChassisType"] = "RackMount";
             asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
-                "/redfish/v1/Chassis/" + chassisId + "/Actions/Chassis.Reset";
+                crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                             chassisId, "Actions",
+                                             "Chassis.Reset");
             asyncResp->res
                 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] =
-                "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
+                crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                             chassisId, "ResetActionInfo");
             asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices";
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "PCIeDevices");
 
             sdbusplus::asio::getProperty<std::vector<std::string>>(
                 *crow::connections::systemBus,
@@ -398,10 +403,12 @@
                 asyncResp->res.jsonValue["Id"] = chassisId;
 #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL
                 asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
-                    "/redfish/v1/Chassis/" + chassisId + "/Thermal";
+                    crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                 chassisId, "Thermal");
                 // Power object
                 asyncResp->res.jsonValue["Power"]["@odata.id"] =
-                    "/redfish/v1/Chassis/" + chassisId + "/Power";
+                    crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                 chassisId, "Power");
 #endif
 #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM
                 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] =
@@ -417,7 +424,8 @@
 #endif
                 // SensorCollection
                 asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
-                    "/redfish/v1/Chassis/" + chassisId + "/Sensors";
+                    crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                 chassisId, "Sensors");
                 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
 
                 nlohmann::json::array_t computerSystems;
@@ -707,8 +715,8 @@
         return;
     }
     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
-    asyncResp->res.jsonValue["@odata.id"] =
-        "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
+    asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Chassis", chassisId, "ResetActionInfo");
     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
 
     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index e0fcb2e..8cdb7e0 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -1636,8 +1636,8 @@
 
     nlohmann::json& jsonResponse = asyncResp->res.jsonValue;
     jsonResponse["Id"] = ifaceId;
-    jsonResponse["@odata.id"] =
-        "/redfish/v1/Managers/bmc/EthernetInterfaces/" + ifaceId;
+    jsonResponse["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Managers", "bmc", "EthernetInterfaces", ifaceId);
     jsonResponse["InterfaceEnabled"] = ethData.nicEnabled;
 
     auto health = std::make_shared<HealthPopulate>(asyncResp);
@@ -1810,9 +1810,9 @@
                 if (found == std::string::npos)
                 {
                     nlohmann::json::object_t iface;
-                    iface["@odata.id"] =
-                        "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
-                        ifaceItem;
+                    iface["@odata.id"] = crow::utility::urlFromPieces(
+                        "redfish", "v1", "Managers", "bmc",
+                        "EthernetInterfaces", ifaceItem);
                     ifaceArray.push_back(std::move(iface));
                 }
             }
@@ -2044,8 +2044,9 @@
             {
                 asyncResp->res.jsonValue["Id"] = ifaceId;
                 asyncResp->res.jsonValue["@odata.id"] =
-                    "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
-                    parentIfaceId + "/VLANs/" + ifaceId;
+                    crow::utility::urlFromPieces(
+                        "redfish", "v1", "Managers", "bmc",
+                        "EthernetInterfaces", parentIfaceId, "VLANs", ifaceId);
 
                 asyncResp->res.jsonValue["VLANEnable"] = ethData.nicEnabled;
                 asyncResp->res.jsonValue["VLANId"] = *ethData.vlanId;
@@ -2230,13 +2231,11 @@
             {
                 if (ifaceItem.starts_with(rootInterfaceName + "_"))
                 {
-                    std::string path =
-                        "/redfish/v1/Managers/bmc/EthernetInterfaces/";
-                    path += rootInterfaceName;
-                    path += "/VLANs/";
-                    path += ifaceItem;
                     nlohmann::json::object_t iface;
-                    iface["@odata.id"] = std::move(path);
+                    iface["@odata.id"] = crow::utility::urlFromPieces(
+                        "redfish", "v1", "Managers", "bmc",
+                        "EthernetInterfaces", rootInterfaceName, "VLANs",
+                        ifaceItem);
                     ifaceArray.push_back(std::move(iface));
                 }
             }
@@ -2244,8 +2243,9 @@
             asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size();
             asyncResp->res.jsonValue["Members"] = std::move(ifaceArray);
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
-                rootInterfaceName + "/VLANs";
+                crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
+                                             "EthernetInterfaces",
+                                             rootInterfaceName, "VLANs");
         });
         });
 
diff --git a/redfish-core/lib/hypervisor_system.hpp b/redfish-core/lib/hypervisor_system.hpp
index b558030..0fd603f 100644
--- a/redfish-core/lib/hypervisor_system.hpp
+++ b/redfish-core/lib/hypervisor_system.hpp
@@ -488,7 +488,8 @@
 {
     jsonResponse["Id"] = ifaceId;
     jsonResponse["@odata.id"] =
-        "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + ifaceId;
+        crow::utility::urlFromPieces("redfish", "v1", "Systems", "hypervisor",
+                                     "EthernetInterfaces", ifaceId);
     jsonResponse["InterfaceEnabled"] = true;
     jsonResponse["MACAddress"] = ethData.macAddress;
 
@@ -823,8 +824,9 @@
                     continue;
                 }
                 nlohmann::json::object_t ethIface;
-                ethIface["@odata.id"] =
-                    "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + name;
+                ethIface["@odata.id"] = crow::utility::urlFromPieces(
+                    "redfish", "v1", "Systems", "hypervisor",
+                    "EthernetInterfaces", name);
                 ifaceArray.push_back(std::move(ethIface));
             }
             asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size();
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index b807bc4..bb9552a 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -1295,8 +1295,9 @@
 
     // Fill in the log entry with the gathered data
     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-    logEntryJson["@odata.id"] =
-        "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
+    logEntryJson["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Systems", "system", "LogServices", "EventLog",
+        "Entries", logEntryID);
     logEntryJson["Name"] = "System Event Log Entry";
     logEntryJson["Id"] = logEntryID;
     logEntryJson["Message"] = std::move(msg);
@@ -1629,9 +1630,9 @@
                 entriesArray.push_back({});
                 nlohmann::json& thisEntry = entriesArray.back();
                 thisEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-                thisEntry["@odata.id"] =
-                    "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
-                    std::to_string(*id);
+                thisEntry["@odata.id"] = crow::utility::urlFromPieces(
+                    "redfish", "v1", "Systems", "system", "LogServices",
+                    "EventLog", "Entries", std::to_string(*id));
                 thisEntry["Name"] = "System Event Log Entry";
                 thisEntry["Id"] = std::to_string(*id);
                 thisEntry["Message"] = *message;
@@ -1750,8 +1751,9 @@
             asyncResp->res.jsonValue["@odata.type"] =
                 "#LogEntry.v1_9_0.LogEntry";
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
-                std::to_string(*id);
+                crow::utility::urlFromPieces(
+                    "redfish", "v1", "Systems", "system", "LogServices",
+                    "EventLog", "Entries", std::to_string(*id));
             asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
             asyncResp->res.jsonValue["Id"] = std::to_string(*id);
             asyncResp->res.jsonValue["Message"] = *message;
@@ -2047,9 +2049,9 @@
 {
     // Fill in the log entry with the gathered data.
     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-    logEntryJson["@odata.id"] =
-        "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
-        logEntryID;
+    logEntryJson["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Systems", "system", "LogServices", "HostLogger",
+        "Entries", logEntryID);
     logEntryJson["Name"] = "Host Logger Entry";
     logEntryJson["Id"] = logEntryID;
     logEntryJson["Message"] = msg;
@@ -2397,9 +2399,9 @@
 
     // Fill in the log entry with the gathered data
     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-    bmcJournalLogEntryJson["@odata.id"] =
-        "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
-        bmcJournalLogEntryID;
+    bmcJournalLogEntryJson["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Managers", "bmc", "LogServices", "Journal", "Entries",
+        bmcJournalLogEntryID);
     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
     bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
     bmcJournalLogEntryJson["Message"] = std::move(message);
@@ -3018,7 +3020,8 @@
             redfishDateTimeOffset.second;
 
         asyncResp->res.jsonValue["Entries"]["@odata.id"] =
-            "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
+            crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
+                                         "LogServices", "Crashdump", "Entries");
         asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
             "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
@@ -3102,8 +3105,9 @@
             logID + "/" + filename;
         nlohmann::json::object_t logEntry;
         logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-        logEntry["@odata.id"] =
-            "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
+        logEntry["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Systems", "system", "LogServices", "Crashdump",
+            "Entries", logID);
         logEntry["Name"] = "CPU Crashdump";
         logEntry["Id"] = logID;
         logEntry["EntryType"] = "Oem";
@@ -3741,9 +3745,9 @@
         // Format entry
         nlohmann::json::object_t bmcLogEntry;
         bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-        bmcLogEntry["@odata.id"] =
-            "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
-            postcodeEntryID;
+        bmcLogEntry["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Systems", "system", "LogServices", "PostCodes",
+            "Entries", postcodeEntryID);
         bmcLogEntry["Name"] = "POST Code Log Entry";
         bmcLogEntry["Id"] = postcodeEntryID;
         bmcLogEntry["Message"] = std::move(msg);
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 376f70c..9da1be0 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -394,6 +394,8 @@
                     }
                 }
 
+                boost::urls::url url = crow::utility::urlFromPieces(
+                    "redfish", "v1", "Managers", "bmc");
                 if (intfPair.first == pidZoneConfigurationIface)
                 {
                     std::string chassis;
@@ -403,11 +405,12 @@
                         chassis = "#IllegalValue";
                     }
                     nlohmann::json& zone = zones[name];
-                    zone["Chassis"]["@odata.id"] =
-                        "/redfish/v1/Chassis/" + chassis;
-                    zone["@odata.id"] =
-                        "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/" +
-                        name;
+                    zone["Chassis"]["@odata.id"] = crow::utility::urlFromPieces(
+                        "redfish", "v1", "Chassis", chassis);
+                    url.set_fragment(
+                        ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name)
+                            .to_string());
+                    zone["@odata.id"] = std::move(url);
                     zone["@odata.type"] = "#OemManager.FanZone";
                     config = &zone;
                 }
@@ -423,10 +426,11 @@
 
                     nlohmann::json& controller = stepwise[name];
                     config = &controller;
-
-                    controller["@odata.id"] =
-                        "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers/" +
-                        name;
+                    url.set_fragment(
+                        ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer /
+                         name)
+                            .to_string());
+                    controller["@odata.id"] = std::move(url);
                     controller["@odata.type"] =
                         "#OemManager.StepwiseController";
 
@@ -448,16 +452,20 @@
                     config = &element;
                     if (isFan)
                     {
-                        element["@odata.id"] =
-                            "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanControllers/" +
-                            name;
+                        url.set_fragment(
+                            ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer /
+                             name)
+                                .to_string());
+                        element["@odata.id"] = std::move(url);
                         element["@odata.type"] = "#OemManager.FanController";
                     }
                     else
                     {
-                        element["@odata.id"] =
-                            "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers/" +
-                            name;
+                        url.set_fragment(
+                            ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer /
+                             name)
+                                .to_string());
+                        element["@odata.id"] = std::move(url);
                         element["@odata.type"] = "#OemManager.PidController";
                     }
                 }
@@ -580,9 +588,14 @@
                             {
                                 dbus::utility::escapePathForDbus(itemCopy);
                                 nlohmann::json::object_t input;
-                                input["@odata.id"] =
-                                    "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/" +
-                                    itemCopy;
+                                boost::urls::url managerUrl =
+                                    crow::utility::urlFromPieces(
+                                        "redfish", "v1", "Managers", "bmc");
+                                managerUrl.set_fragment(
+                                    ("/Oem/OpenBmc/Fan/FanZones"_json_pointer /
+                                     itemCopy)
+                                        .to_string());
+                                input["@odata.id"] = std::move(managerUrl);
                                 data.push_back(std::move(input));
                             }
                         }
@@ -2043,12 +2056,14 @@
             aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
             nlohmann::json::array_t managerForChassis;
             nlohmann::json::object_t managerObj;
-            managerObj["@odata.id"] = "/redfish/v1/Chassis/" + chassisId;
+            boost::urls::url chassiUrl = crow::utility::urlFromPieces(
+                "redfish", "v1", "Chassis", chassisId);
+            managerObj["@odata.id"] = chassiUrl;
             managerForChassis.push_back(std::move(managerObj));
             aRsp->res.jsonValue["Links"]["ManagerForChassis"] =
                 std::move(managerForChassis);
             aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] =
-                "/redfish/v1/Chassis/" + chassisId;
+                chassiUrl;
         });
 
         static bool started = false;
diff --git a/redfish-core/lib/memory.hpp b/redfish-core/lib/memory.hpp
index e55eded..6e77816 100644
--- a/redfish-core/lib/memory.hpp
+++ b/redfish-core/lib/memory.hpp
@@ -760,8 +760,8 @@
         }
         // Set @odata only if object is found
         aResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
-        aResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Systems/system/Memory/" + dimmId;
+        aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Systems", "system", "Memory", dimmId);
         return;
         });
 }
diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp
index 41e7e93..41464bc 100644
--- a/redfish-core/lib/message_registries.hpp
+++ b/redfish-core/lib/message_registries.hpp
@@ -111,7 +111,7 @@
     }
 
     asyncResp->res.jsonValue["@odata.id"] =
-        "/redfish/v1/Registries/" + registry;
+        crow::utility::urlFromPieces("redfish", "v1", "Registries", registry);
     asyncResp->res.jsonValue["@odata.type"] =
         "#MessageRegistryFile.v1_1_0.MessageRegistryFile";
     asyncResp->res.jsonValue["Name"] = registry + " Message Registry File";
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 77afd0a..5332a39 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -67,8 +67,8 @@
                 continue;
             }
             nlohmann::json::object_t pcieDevice;
-            pcieDevice["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices/" + devName;
+            pcieDevice["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "Systems", "system", "PCIeDevices", devName);
             pcieDeviceList.push_back(std::move(pcieDevice));
         }
         asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
@@ -242,13 +242,15 @@
             asyncResp->res.jsonValue["@odata.type"] =
                 "#PCIeDevice.v1_4_0.PCIeDevice";
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices/" + device;
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "PCIeDevices", device);
             asyncResp->res.jsonValue["Name"] = "PCIe Device";
             asyncResp->res.jsonValue["Id"] = device;
 
             asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices/" + device +
-                "/PCIeFunctions";
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "PCIeDevices", device,
+                                             "PCIeFunctions");
         };
         std::string escapedPath = std::string(pciePath) + "/" + device;
         dbus::utility::escapePathForDbus(escapedPath);
@@ -277,9 +279,9 @@
 
         asyncResp->res.jsonValue["@odata.type"] =
             "#PCIeFunctionCollection.PCIeFunctionCollection";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Systems/system/PCIeDevices/" + device +
-            "/PCIeFunctions";
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Systems", "system", "PCIeDevices", device,
+            "PCIeFunctions");
         asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
         asyncResp->res.jsonValue["Description"] =
             "Collection of PCIe Functions for PCIe Device " + device;
@@ -330,9 +332,9 @@
                     continue;
                 }
                 nlohmann::json::object_t pcieFunction;
-                pcieFunction["@odata.id"] =
-                    "/redfish/v1/Systems/system/PCIeDevices/" + device +
-                    "/PCIeFunctions/" + std::to_string(functionNum);
+                pcieFunction["@odata.id"] = crow::utility::urlFromPieces(
+                    "redfish", "v1", "Systems", "system", "PCIeDevices", device,
+                    "PCIeFunctions", std::to_string(functionNum));
                 pcieFunctionList.push_back(std::move(pcieFunction));
             }
             asyncResp->res.jsonValue["Members@odata.count"] =
@@ -406,13 +408,15 @@
             asyncResp->res.jsonValue["@odata.type"] =
                 "#PCIeFunction.v1_2_0.PCIeFunction";
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices/" + device +
-                "/PCIeFunctions/" + function;
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "PCIeDevices", device,
+                                             "PCIeFunctions", function);
             asyncResp->res.jsonValue["Name"] = "PCIe Function";
             asyncResp->res.jsonValue["Id"] = function;
             asyncResp->res.jsonValue["FunctionId"] = std::stoi(function);
             asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
-                "/redfish/v1/Systems/system/PCIeDevices/" + device;
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "PCIeDevices", device);
 
             for (const auto& property : pcieDevProperties)
             {
diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp
index 1ac29ff..8b1bfa9 100644
--- a/redfish-core/lib/processor.hpp
+++ b/redfish-core/lib/processor.hpp
@@ -551,10 +551,10 @@
         if (appliedConfig != nullptr)
         {
             const std::string& dbusPath = appliedConfig->str;
-            std::string uri = "/redfish/v1/Systems/system/Processors/" + cpuId +
-                              "/OperatingConfigs";
             nlohmann::json::object_t operatingConfig;
-            operatingConfig["@odata.id"] = uri;
+            operatingConfig["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "Systems", "system", "Processors", cpuId,
+                "OperatingConfigs");
             json["OperatingConfigs"] = std::move(operatingConfig);
 
             // Reuse the D-Bus config object name for the Redfish
@@ -569,10 +569,10 @@
                 messages::internalError(aResp->res);
                 return;
             }
-            uri += '/';
-            uri += dbusPath.substr(baseNamePos + 1);
             nlohmann::json::object_t appliedOperatingConfig;
-            appliedOperatingConfig["@odata.id"] = uri;
+            appliedOperatingConfig["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "Systems", "system", "Processors", cpuId,
+                "OperatingConfigs", dbusPath.substr(baseNamePos + 1));
             json["AppliedOperatingConfig"] = std::move(appliedOperatingConfig);
 
             // Once we found the current applied config, queue another
@@ -1256,8 +1256,8 @@
             "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
         asyncResp->res.jsonValue["@odata.type"] =
             "#Processor.v1_11_0.Processor";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Systems/system/Processors/" + processorId;
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Systems", "system", "Processors", processorId);
 
         getProcessorObject(
             asyncResp, processorId,
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index 5ae6674..9dcb873 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -31,8 +31,8 @@
 {
     res.jsonValue["Id"] = session.uniqueId;
     res.jsonValue["UserName"] = session.username;
-    res.jsonValue["@odata.id"] =
-        "/redfish/v1/SessionService/Sessions/" + session.uniqueId;
+    res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "SessionService", "Sessions", session.uniqueId);
     res.jsonValue["@odata.type"] = "#Session.v1_5_0.Session";
     res.jsonValue["Name"] = "User Session";
     res.jsonValue["Description"] = "Manager User Session";
@@ -127,7 +127,8 @@
     for (const std::string* uid : sessionIds)
     {
         nlohmann::json::object_t session;
-        session["@odata.id"] = "/redfish/v1/SessionService/Sessions/" + *uid;
+        session["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "SessionService", "Sessions", *uid);
         ret.push_back(std::move(session));
     }
     return ret;
diff --git a/redfish-core/lib/roles.hpp b/redfish-core/lib/roles.hpp
index 413cd8c..fe9110c 100644
--- a/redfish-core/lib/roles.hpp
+++ b/redfish-core/lib/roles.hpp
@@ -93,8 +93,8 @@
         asyncResp->res.jsonValue["IsPredefined"] = true;
         asyncResp->res.jsonValue["Id"] = roleId;
         asyncResp->res.jsonValue["RoleId"] = roleId;
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/AccountService/Roles/" + roleId;
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "AccountService", "Roles", roleId);
         asyncResp->res.jsonValue["AssignedPrivileges"] = std::move(privArray);
         });
 }
@@ -137,8 +137,8 @@
                 if (!role.empty())
                 {
                     nlohmann::json::object_t member;
-                    member["@odata.id"] =
-                        "/redfish/v1/AccountService/Roles/" + role;
+                    member["@odata.id"] = crow::utility::urlFromPieces(
+                        "redfish", "v1", "AccountService", "Roles", role);
                     memberArray.push_back(std::move(member));
                 }
             }
diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
index e6d7a7a..3ff4842 100644
--- a/redfish-core/lib/sensors.hpp
+++ b/redfish-core/lib/sensors.hpp
@@ -559,8 +559,8 @@
         }
         populateChassisNode(asyncResp->res.jsonValue, chassisSubNode);
 
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Chassis/" + chassisIdStr + "/" + chassisSubNode;
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Chassis", chassisIdStr, chassisSubNode);
 
         // Get the list of all sensors for this Chassis element
         std::string sensorPath = *chassisPath + "/all_sensors";
@@ -1128,10 +1128,12 @@
                                                 .jsonValue["Redundancy"];
 
                     nlohmann::json::object_t redundancy;
-                    redundancy["@odata.id"] =
-                        "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId +
-                        "/" + sensorsAsyncResp->chassisSubNode +
-                        "#/Redundancy/" + std::to_string(jResp.size());
+                    boost::urls::url url = crow::utility::urlFromPieces(
+                        "redfish", "v1", "Chassis", sensorsAsyncResp->chassisId,
+                        sensorsAsyncResp->chassisSubNode);
+                    url.set_fragment(("/Redundancy"_json_pointer / jResp.size())
+                                         .to_string());
+                    redundancy["@odata.id"] = std::move(url);
                     redundancy["@odata.type"] = "#Redundancy.v1_3_2.Redundancy";
                     redundancy["MinNumNeeded"] = minNumNeeded;
                     redundancy["MemberId"] = name;
@@ -1179,7 +1181,7 @@
                 std::string* value = odata->get_ptr<std::string*>();
                 if (value != nullptr)
                 {
-                    *value += std::to_string(count);
+                    *value += "/" + std::to_string(count);
                     count++;
                     sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
                 }
@@ -2199,8 +2201,10 @@
     // Add new PowerSupply object to JSON array
     powerSupplyArray.push_back({});
     nlohmann::json& powerSupply = powerSupplyArray.back();
-    powerSupply["@odata.id"] =
-        "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/";
+    boost::urls::url url = crow::utility::urlFromPieces(
+        "redfish", "v1", "Chassis", chassisId, "Power");
+    url.set_fragment(("/PowerSupplies"_json_pointer).to_string());
+    powerSupply["@odata.id"] = std::move(url);
     powerSupply["MemberId"] = inventoryItem.name;
     powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " ");
     powerSupply["Manufacturer"] = inventoryItem.manufacturer;
@@ -2383,11 +2387,13 @@
                             // PowerControl. Follows MemberId naming and
                             // naming in power.hpp.
                             nlohmann::json::object_t power;
-                            power["@odata.id"] =
-                                "/redfish/v1/Chassis/" +
-                                sensorsAsyncResp->chassisId + "/" +
-                                sensorsAsyncResp->chassisSubNode + "#/" +
-                                fieldName + "/0";
+                            boost::urls::url url = crow::utility::urlFromPieces(
+                                "redfish", "v1", "Chassis",
+                                sensorsAsyncResp->chassisId,
+                                sensorsAsyncResp->chassisSubNode);
+                            url.set_fragment((""_json_pointer / fieldName / "0")
+                                                 .to_string());
+                            power["@odata.id"] = std::move(url);
                             tempArray.push_back(std::move(power));
                         }
                         sensorJson = &(tempArray.back());
@@ -2423,11 +2429,13 @@
                     else
                     {
                         nlohmann::json::object_t member;
-                        member["@odata.id"] = "/redfish/v1/Chassis/" +
-                                              sensorsAsyncResp->chassisId +
-                                              "/" +
-                                              sensorsAsyncResp->chassisSubNode +
-                                              "#/" + fieldName + "/";
+                        boost::urls::url url = crow::utility::urlFromPieces(
+                            "redfish", "v1", "Chassis",
+                            sensorsAsyncResp->chassisId,
+                            sensorsAsyncResp->chassisSubNode);
+                        url.set_fragment(
+                            (""_json_pointer / fieldName).to_string());
+                        member["@odata.id"] = std::move(url);
                         tempArray.push_back(std::move(member));
                         sensorJson = &(tempArray.back());
                     }
diff --git a/redfish-core/lib/storage.hpp b/redfish-core/lib/storage.hpp
index 93911e5..88ced62 100644
--- a/redfish-core/lib/storage.hpp
+++ b/redfish-core/lib/storage.hpp
@@ -102,9 +102,9 @@
             }
 
             nlohmann::json::object_t driveJson;
-            driveJson["@odata.id"] =
-                "/redfish/v1/Systems/system/Storage/1/Drives/" +
-                object.filename();
+            driveJson["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "Systems", "system", "Storage", "1", "Drives",
+                object.filename());
             driveArray.push_back(std::move(driveJson));
         }
 
@@ -158,8 +158,10 @@
             storageController["@odata.type"] =
                 "#Storage.v1_7_0.StorageController";
             storageController["@odata.id"] =
-                "/redfish/v1/Systems/system/Storage/1#/StorageControllers/" +
-                std::to_string(index);
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "Storage", "1")
+                    .set_fragment(("/StorageControllers"_json_pointer / index)
+                                      .to_string());
             storageController["Name"] = id;
             storageController["MemberId"] = id;
             storageController["Status"]["State"] = "Enabled";
@@ -607,7 +609,9 @@
 
             asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
             asyncResp->res.jsonValue["@odata.id"] =
-                "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
+                crow::utility::urlFromPieces("redfish", "v1", "Systems",
+                                             "system", "Storage", "1", "Drives",
+                                             driveId);
             asyncResp->res.jsonValue["Name"] = driveId;
             asyncResp->res.jsonValue["Id"] = driveId;
 
@@ -623,7 +627,8 @@
                 asyncResp, [](const std::string& chassisId,
                               const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
                     aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
-                        "/redfish/v1/Chassis/" + chassisId;
+                        crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                     chassisId);
                 });
 
             // default it to Enabled
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index d6f4fba..06729fd 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -3000,7 +3000,8 @@
                             const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
             nlohmann::json::array_t chassisArray;
             nlohmann::json& chassis = chassisArray.emplace_back();
-            chassis["@odata.id"] = "/redfish/v1/Chassis/" + chassisId;
+            chassis["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "Chassis", chassisId);
             aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray);
         });
 
diff --git a/redfish-core/lib/task.hpp b/redfish-core/lib/task.hpp
index 950eac3..563f06f 100644
--- a/redfish-core/lib/task.hpp
+++ b/redfish-core/lib/task.hpp
@@ -409,8 +409,8 @@
         }
         asyncResp->res.jsonValue["TaskStatus"] = ptr->status;
         asyncResp->res.jsonValue["Messages"] = ptr->messages;
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/TaskService/Tasks/" + strParam;
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "TaskService", "Tasks", strParam);
         if (!ptr->gave204)
         {
             asyncResp->res.jsonValue["TaskMonitor"] =
@@ -456,8 +456,9 @@
                 continue; // shouldn't be possible
             }
             nlohmann::json::object_t member;
-            member["@odata.id"] =
-                "redfish/v1/TaskService/Tasks/" + std::to_string(task->index);
+            member["@odata.id"] = crow::utility::urlFromPieces(
+                "redfish", "v1", "TaskService", "Tasks",
+                std::to_string(task->index));
             members.emplace_back(std::move(member));
         }
         });
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index cf5ac0d..c7b1593 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -786,8 +786,9 @@
 
                 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
                 nlohmann::json::object_t member;
-                member["@odata.id"] =
-                    "/redfish/v1/UpdateService/FirmwareInventory/" + swId;
+                member["@odata.id"] = crow::utility::urlFromPieces(
+                    "redfish", "v1", "UpdateService", "FirmwareInventory",
+                    swId);
                 members.push_back(std::move(member));
                 asyncResp->res.jsonValue["Members@odata.count"] =
                     members.size();
@@ -909,8 +910,8 @@
         std::shared_ptr<std::string> swId =
             std::make_shared<std::string>(param);
 
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "UpdateService", "FirmwareInventory", *swId);
 
         constexpr std::array<std::string_view, 1> interfaces = {
             "xyz.openbmc_project.Software.Version"};