| 1799 | } |
| 1800 | |
| 1801 | void RenderingController::HandlePUT_mxnLayout(const httplib::Request& req, httplib::Response& res) const |
| 1802 | { |
| 1803 | if (req.body.empty()) |
| 1804 | { |
| 1805 | const auto error = ErrorResponse::InvalidRequest( |
| 1806 | "Request body must be a v2.0 layout document.", req.path); |
| 1807 | this->SendErrorResponse(res, 400, error); |
| 1808 | return; |
| 1809 | } |
| 1810 | |
| 1811 | // Pre-parse the body for syntax validation. Schema / structural validation |
| 1812 | // is done by the engine's ApplyLayout via the bridge; duplicating it here |
| 1813 | // would drift. nlohmann::parse failures surface as 400. |
| 1814 | try |
| 1815 | { |
| 1816 | [[maybe_unused]] const auto parsed = nlohmann::json::parse(req.body); |
| 1817 | } |
| 1818 | catch (const nlohmann::json::exception& e) |
| 1819 | { |
| 1820 | const auto error = ErrorResponse::InvalidRequest( |
| 1821 | std::string("Invalid JSON body: ") + e.what(), req.path); |
| 1822 | this->SendErrorResponse(res, 400, error); |
| 1823 | return; |
| 1824 | } |
| 1825 | |
| 1826 | if (m_RenderWindowBridge == nullptr || !m_RenderWindowBridge->HasMxNLayoutSetter()) |
| 1827 | { |
| 1828 | const auto error = ErrorResponse::RenderWindowNotAvailable(req.path); |
| 1829 | this->SendErrorResponse(res, 503, error); |
| 1830 | return; |
| 1831 | } |
| 1832 | |
| 1833 | std::string serialized; |
| 1834 | try |
| 1835 | { |
| 1836 | serialized = m_RenderWindowBridge->SetMxNLayout(req.body); |
| 1837 | } |
| 1838 | // Layout-specific catch: every mitk::Exception from ApplyLayout is treated |
| 1839 | // as a document-shape failure. Caught BEFORE the generic std::exception so |
| 1840 | // we don't fall through to MapBridgeException, which would map it to 422. |
| 1841 | // If the engine broadens ApplyLayout's failure model in the future, narrow |
| 1842 | // this catch. |
| 1843 | catch (const mitk::Exception& e) |
| 1844 | { |
| 1845 | const auto error = ErrorResponse::InvalidRequest( |
| 1846 | std::string("Layout document rejected: ") + e.what(), req.path); |
| 1847 | this->SendErrorResponse(res, 400, error); |
| 1848 | return; |
| 1849 | } |
| 1850 | catch (const std::exception& e) |
| 1851 | { |
| 1852 | const auto [status, payload] = MapBridgeException(e, req.path); |
| 1853 | this->SendErrorResponse(res, status, payload); |
| 1854 | return; |
| 1855 | } |
| 1856 | |
| 1857 | // 200 + body (not 204): clients can refresh their cell name list from the |
| 1858 | // response without an additional GET. |