{"id":1911,"date":"2025-11-21T10:00:01","date_gmt":"2025-11-21T15:00:01","guid":{"rendered":"http:\/\/www.mymiller.name\/wordpress\/?p=1911"},"modified":"2025-11-21T10:00:01","modified_gmt":"2025-11-21T15:00:01","slug":"http-server-contexthandler-jar","status":"publish","type":"post","link":"https:\/\/www.mymiller.name\/wordpress\/java_http\/http-server-contexthandler-jar\/","title":{"rendered":"HTTP Server ContextHandler in a Jar"},"content":{"rendered":"\n<p>Last time I showed you how to create a basic HTTP Server that you can put into a JAR easily. &nbsp;Today I&#8217;m going to show you how to extend that to include a ContextHandler and AbstractContextHandler to make it easier to create Handlers for your HTTP Server. &nbsp;If you remember our properties file from before had a &#8220;Handlers&#8221; property which was a comma separated list of classes to load as ContextHandlers for our HTTP Server.<\/p>\n\n\n\n<p>Those ContextHandlers are load and initialized in the HttpSystem.start() method. &nbsp;It takes a&nbsp;ContextHandlerInterface. &nbsp;Here is the implementation for that interface:\n<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package name.mymiller.httpserver.handlers;\n\nimport java.util.List;\n\nimport com.sun.net.httpserver.Filter;\nimport com.sun.net.httpserver.HttpHandler;\n\n\/**\n * @author jmiller Interface to create ContextHandler for the HTTP Server\n *\/\n@SuppressWarnings(\"restriction\")\npublic interface ContextHandlerInterface extends HttpHandler\n{\n\t\/**\n\t *\n\t * @return List of Filter's this handler needs.\n\t *\/\n\tList&lt;Filter> getFilters();\n}\n<\/code><\/pre>\n\n\n\n<p>This is really a simple interface. &nbsp;First notice it extends HttpHandler, this is important and our derived classes will need to implement that API which is a single HttpHandler.handle(HttpExchange exchange). &nbsp;Next we have a method ContextHandlerInterface.getContext() for returning the Context of this handler. &nbsp;This is important as our HttpSystem needs to know the URI this ContextHandler will be processing. &nbsp;Next we have a method ContextHandlerInterface.getFilters() that returns any filters this context needs. &nbsp;We will discuss those in another post later on.<\/p>\n\n\n\n<p>Now we create an AbstractContextHandler to take care of some of the more mundane items that we don&#8217;t want to keep implementing. &nbsp;Here is the code for AbstractContextHandler:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package name.mymiller.httpserver.handlers;\n\nimport java.io.BufferedInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.Map;\n\nimport com.sun.net.httpserver.HttpExchange;\n\nimport name.mymiller.extensions.lang.AdvancedString;\nimport name.mymiller.extensions.log.LogManager;\nimport name.mymiller.httpserver.HttpConstants;\n\n\/**\n * @author jmiller Abstract class to build handlers for URI Requests coming into\n *         the HTTPS erver\n *\/\n@SuppressWarnings(\"restriction\")\npublic abstract class AbstractContextHandler implements ContextHandlerInterface\n{\n\t\/**\n\t * Method to handle Delete URI Requests\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the URI information\n\t * @param pathInfo\n\t *            URI Path specified\n\t * @param parameters\n\t *            Parameters parsed fromt he URI Quests\n\t *\/\n\tpublic void doDelete(final HttpExchange exchange, final String pathInfo, final Map&lt;String, Object> parameters) {\n\n\t}\n\n\t\/**\n\t * Method to handle Get URI Requests\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the URI information\n\t * @param pathInfo\n\t *            URI Path specified\n\t * @param parameters\n\t *            Parameters parsed fromt he URI Quests\n\t * @throws IOException\n\t *             Error responding to request\n\t *\/\n\n\tpublic void doGet(final HttpExchange exchange, final String pathInfo, final Map&lt;String, Object> parameters)\n\t\t\tthrows IOException\n\t{\n\n\t}\n\n\t\/**\n\t * Method to handle Head URI Requests\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the URI information\n\t * @param pathInfo\n\t *            URI Path specified\n\t * @param parameters\n\t *            Parameters parsed fromt he URI Quests\n\t * @throws IOException\n\t *             Error responding to request\n\t *\/\n\n\tpublic void doHead(final HttpExchange exchange, final String pathInfo, final Map&lt;String, Object> parameters)\n\t\t\tthrows IOException\n\t{\n\n\t}\n\n\t\/**\n\t * Method to handle Post URI Requests\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the URI information\n\t * @param pathInfo\n\t *            URI Path specified\n\t * @param parameters\n\t *            Parameters parsed fromt he URI Quests\n\t * @throws IOException\n\t *             Error responding to request\n\t *\/\n\n\tpublic void doPost(final HttpExchange exchange, final String pathInfo, final Map&lt;String, Object> parameters)\n\t\t\tthrows IOException\n\t{\n\n\t}\n\n\t\/**\n\t * Method to handle Put URI Requests\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the URI information\n\t * @param pathInfo\n\t *            URI Path specified\n\t * @param parameters\n\t *            Parameters parsed fromt he URI Quests\n\t *\/\n\n\tpublic void doPut(final HttpExchange exchange, final String pathInfo, final Map&lt;String, Object> parameters) {\n\n\t}\n\n\t\/*\n\t * (non-Javadoc)\n\t *\n\t * @see com.sun.net.httpserver.HttpHandler#handle(com.sun.net.httpserver.\n\t * HttpExchange)\n\t *\/\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void handle(final HttpExchange exchange) throws IOException\n\t{\n\t\tfinal String context = exchange.getHttpContext().getPath();\n\t\tfinal String pathInfo = exchange.getRequestURI().getRawPath().substring(context.length());\n\t\tfinal AdvancedString advPathInfo = new AdvancedString(pathInfo);\n\t\tif (advPathInfo.containsHtml())\n\t\t{\n\t\t\tthis.sendBadRequest(exchange);\n\t\t} else\n\t\t{\n\t\t\tMap&lt;String, Object> parameters = null;\n\t\t\tif (exchange.getAttribute(\"parameters\") instanceof Map&lt;?, ?>)\n\t\t\t{\n\t\t\t\tparameters = (Map&lt;String, Object>) exchange.getAttribute(\"parameters\");\n\t\t\t}\n\n\t\t\tif (this.isDelete(exchange))\n\t\t\t{\n\t\t\t\tthis.doDelete(exchange, pathInfo, parameters);\n\t\t\t} else if (this.isGet(exchange))\n\t\t\t{\n\t\t\t\tthis.doGet(exchange, pathInfo, parameters);\n\t\t\t} else if (this.isHead(exchange))\n\t\t\t{\n\t\t\t\tthis.doHead(exchange, pathInfo, parameters);\n\t\t\t} else if (this.isPost(exchange))\n\t\t\t{\n\t\t\t\tthis.doPost(exchange, pathInfo, parameters);\n\t\t\t} else if (this.isPut(exchange))\n\t\t\t{\n\t\t\t\tthis.doPut(exchange, pathInfo, parameters);\n\t\t\t} else\n\t\t\t{\n\t\t\t\tthis.sendBadRequest(exchange);\n\t\t\t}\n\t\t}\n\t}\n\n\t\/**\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @return True if this is a Delete URI Request\n\t *\/\n\tprivate boolean isDelete(final HttpExchange exchange)\n\t{\n\t\treturn HttpConstants.DELETE_REQUEST.equalsIgnoreCase(exchange.getRequestMethod());\n\t}\n\n\t\/**\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @return True if this is a Get URI Request\n\t *\/\n\n\tprivate boolean isGet(final HttpExchange exchange)\n\t{\n\t\treturn HttpConstants.GET_REQUEST.equalsIgnoreCase(exchange.getRequestMethod());\n\t}\n\n\t\/**\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @return True if this is a Head URI Request\n\t *\/\n\n\tprivate boolean isHead(final HttpExchange exchange)\n\t{\n\t\treturn HttpConstants.HEAD_REQUEST.equalsIgnoreCase(exchange.getRequestMethod());\n\t}\n\n\t\/**\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @return True if this is a Post URI Request\n\t *\/\n\n\tprivate boolean isPost(final HttpExchange exchange)\n\t{\n\t\treturn HttpConstants.POST_REQUEST.equalsIgnoreCase(exchange.getRequestMethod());\n\t}\n\n\t\/**\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @return True if this is a Put URI Request\n\t *\/\n\n\tprivate boolean isPut(final HttpExchange exchange)\n\t{\n\t\treturn HttpConstants.PUT_REQUEST.equalsIgnoreCase(exchange.getRequestMethod());\n\t}\n\n\t\/**\n\t * Send an HTTP Error for Bad Request\n\t *\n\t * @param exchange\n\t *            HttpExchange containing the information on this request\n\t * @throws IOException\n\t *             Error responding to request\n\t *\n\t *\/\n\tprivate void sendBadRequest(final HttpExchange exchange) throws IOException\n\t{\n\t\tLogManager.getLogger(this.getClass()).info(\"HTML found in PathInfo, rejecting\");\n\t\texchange.sendResponseHeaders(HttpConstants.HTTP_BAD_REQUEST_STATUS, 0);\n\t\t\/\/ Write the response string\n\t\tfinal OutputStream os = exchange.getResponseBody();\n\t\tos.write(\"Bad Request Status\".getBytes());\n\t\tos.close();\n\t}\n\n\t\/**\n\t * Method to send the HTTP Response\n\t *\n\t * @param exchange\n\t *            HttpExchange to send the response\n\t * @param responseCode\n\t *            Response code, one of HttpConstants\n\t * @param responseBody\n\t *            byte&#91;] containing the response to send\n\t * @throws IOException\n\t *             Error in sending response.\n\t *\/\n\tprotected void sendResponse(final HttpExchange exchange, final int responseCode, final byte&#91;] responseBody)\n\t\t\tthrows IOException\n\t{\n\t\tthis.sendResponse(exchange, responseCode, responseBody.length);\n\n\t\tif (responseBody.length > 0)\n\t\t{\n\t\t\tfinal OutputStream os = exchange.getResponseBody();\n\t\t\tos.write(responseBody);\n\t\t\tos.close();\n\t\t}\n\t}\n\n\t\/**\n\t * Method to send the HTTP Response\n\t *\n\t * @param exchange\n\t *            HttpExchange to send the response\n\t * @param responseCode\n\t *            Response code, one of HttpConstants\n\t * @param inputStream\n\t *            inputStream containing the response to send\n\t * @throws IOException\n\t *             Error in sending response.\n\t *\/\n\tprotected void sendResponse(final HttpExchange exchange, final int responseCode, final InputStream inputStream)\n\t\t\tthrows IOException\n\t{\n\t\tfinal byte&#91;] bytesToSend = new byte&#91;inputStream.available()];\n\n\t\tfinal BufferedInputStream bis = new BufferedInputStream(inputStream);\n\t\tbis.read(bytesToSend, 0, bytesToSend.length);\n\t\tbis.close();\n\n\t\tthis.sendResponse(exchange, responseCode, bytesToSend);\n\t}\n\n\t\/**\n\t * Method to send the HTTP Response Header with the size of the response,\n\t *\n\t * @param exchange\n\t *            HttpExchange to send the response\n\t * @param responseCode\n\t *            Response code, one of HttpConstants\n\t * @param responseSize\n\t *            Int indicating the size of the response\n\t * @throws IOException\n\t *             Error in sending response.\n\t *\/\n\tprotected void sendResponse(final HttpExchange exchange, final int responseCode, final long responseSize)\n\t\t\tthrows IOException\n\t{\n\t\texchange.sendResponseHeaders(responseCode, responseSize);\n\t}\n\n\t\/**\n\t * Method to send the HTTP Response\n\t *\n\t * @param exchange\n\t *            HttpExchange to send the response\n\t * @param responseCode\n\t *            Response code, one of HttpConstants\n\t * @param responseBody\n\t *            String containing the response to send\n\t * @throws IOException\n\t *             Error in sending response.\n\t *\/\n\tprotected void sendResponse(final HttpExchange exchange, final int responseCode, final String responseBody)\n\t\t\tthrows IOException\n\t{\n\t\tif (responseBody != null)\n\t\t{\n\t\t\tthis.sendResponse(exchange, responseCode, responseBody.getBytes());\n\t\t} else\n\t\t{\n\t\t\tthis.sendResponse(exchange, responseCode, new byte&#91;0]);\n\t\t}\n\t}\n}\n<\/code><\/pre>\n\n\n\n<p>First thing to notice is that we implement the HttpHandler.handle(HttpExchange exchange) method. &nbsp;It uses the implemented helper functions to determine the type of HTTP Request that is incoming, and calls out to stub functions based on type; HEAD, GET, POST, PUT, &amp; DELETE. &nbsp;All you need to do is override the methods your interested in and implement your code. Now we use an AdvancedString to determine if there is any HTML embedded in the URL, if so we abort the request and send an HTTP_BAD_REQUEST_STATUS.<\/p>\n\n\n\n<p>Now your ready to implement your own ContextHandlers, by extending AbstractContextHandler, and implement the&nbsp;ContextHandlerInterface.getContext() &amp;&nbsp;ContextHandlerInterface.getFilters() methods to provide your specifics. &nbsp;Then override the doHead(), doGet(), doPost(), doPut(), and doDelete() &nbsp;to implement the final pieces of your ContextHandler. You have multiple methods of sendResponse() to use for sending your response.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time I showed you how to create a basic HTTP Server that you can put into a JAR easily. &nbsp;Today I&#8217;m going to show you how to extend that to include a ContextHandler and AbstractContextHandler to make it easier to create Handlers for your HTTP Server. &nbsp;If you remember our properties file from before [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1902,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[459],"tags":[69],"series":[226],"class_list":["post-1911","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java_http","tag-java-2","series-http-server-in-a-jar"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1","jetpack-related-posts":[{"id":1901,"url":"https:\/\/www.mymiller.name\/wordpress\/java_http\/http-server-jar\/","url_meta":{"origin":1911,"position":0},"title":"HTTP Server in your Jar","author":"Jeffery Miller","date":"November 20, 2025","format":false,"excerpt":"I recently had the need to be able to access a couple of files from a number of computers and didn't want to setup network shares and things. \u00a0These were a simple text files, that I was logging information into. However I was going to be moving around a number\u2026","rel":"","context":"In &quot;Java HTTP&quot;","block_context":{"text":"Java HTTP","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_http\/"},"img":{"alt_text":"filters","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":1906,"url":"https:\/\/www.mymiller.name\/wordpress\/java_http\/http-server-command-handler-in-a-jar\/","url_meta":{"origin":1911,"position":1},"title":"HTTP Server Command Handler in a Jar","author":"Jeffery Miller","date":"November 24, 2025","format":false,"excerpt":"Today, I'm going to share with you a command handler for the HTTP Server I presented previously. \u00a0This is a handler to extend the abilities of that HTTP Server in a Jar to provide you the ability to stop, restart and get status of it, if needed. WARNING: This is\u2026","rel":"","context":"In &quot;Java HTTP&quot;","block_context":{"text":"Java HTTP","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_http\/"},"img":{"alt_text":"filters","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":1932,"url":"https:\/\/www.mymiller.name\/wordpress\/java_http\/http-server-filters-jar\/","url_meta":{"origin":1911,"position":2},"title":"HTTP Server Filters in a Jar","author":"Jeffery Miller","date":"November 24, 2025","format":false,"excerpt":"We discussed the HTTP Server ContextHandler in a Jar\u00a0one of the methods was ContextHandlerInterface.getFilters() this method returns a list of Filters to apply to incoming HTTP Requests. \u00a0We subclass\u00a0com.sun.net.httpserver.Filter for these classes. These can be used to do processing on the request and add attributes to the HTTP Exchange. Filters\u2026","rel":"","context":"In &quot;Java HTTP&quot;","block_context":{"text":"Java HTTP","link":"https:\/\/www.mymiller.name\/wordpress\/category\/java_http\/"},"img":{"alt_text":"filters","src":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2016\/09\/http-875180_640.jpg?fit=640%2C452&ssl=1&resize=525%2C300 1.5x"},"classes":[]},{"id":3947,"url":"https:\/\/www.mymiller.name\/wordpress\/spring\/spring4\/goodbye-boilerplate-mastering-declarative-http-clients-in-spring-boot\/","url_meta":{"origin":1911,"position":3},"title":"Goodbye Boilerplate: Mastering Declarative HTTP Clients in Spring Boot","author":"Jeffery Miller","date":"December 19, 2025","format":false,"excerpt":"For years, calling remote REST APIs in Spring Boot meant one of two things: wrestling with the aging, blocking RestTemplate, or writing verbose, reactive boilerplate with WebClient. While libraries like Spring Cloud Feign offered a cleaner, declarative approach, they required extra dependencies and configuration. With the arrival of Spring Framework\u2026","rel":"","context":"In &quot;Spring4&quot;","block_context":{"text":"Spring4","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring\/spring4\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/putty-3678638_1280.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/putty-3678638_1280.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/putty-3678638_1280.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/putty-3678638_1280.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/12\/putty-3678638_1280.avif 3x"},"classes":[]},{"id":3828,"url":"https:\/\/www.mymiller.name\/wordpress\/spring-gateway\/load-balancing-your-microservices-configuring-spring-cloud-gateway-with-spring-discovery-server\/","url_meta":{"origin":1911,"position":4},"title":"Load Balancing Your Microservices: Configuring Spring Cloud Gateway with Spring Discovery Server","author":"Jeffery Miller","date":"December 24, 2025","format":false,"excerpt":"In a microservices architecture, ensuring high availability and distributing traffic evenly across multiple instances of a service is paramount. Spring Cloud Gateway, when integrated with a Spring Discovery Server (like Netflix Eureka, Consul, or Spring Cloud Service Registry), provides a powerful and straightforward way to achieve client-side load balancing without\u2026","rel":"","context":"In &quot;Spring Gateway&quot;","block_context":{"text":"Spring Gateway","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring-gateway\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/04\/meditation-3814069_1280.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/04\/meditation-3814069_1280.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/04\/meditation-3814069_1280.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/04\/meditation-3814069_1280.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/04\/meditation-3814069_1280.avif 3x"},"classes":[]},{"id":3871,"url":"https:\/\/www.mymiller.name\/wordpress\/spring_events\/tracking-user-lifecycle-capturing-login-failed-login-and-signup-events-in-spring-authorization-server\/","url_meta":{"origin":1911,"position":5},"title":"Tracking User Lifecycle: Capturing Login, Failed Login, and Signup Events in Spring Authorization Server","author":"Jeffery Miller","date":"November 24, 2025","format":false,"excerpt":"Understanding how users interact with your Spring Authorization Server is crucial for security, auditing, and gaining insights into user behavior. By capturing key lifecycle events like successful logins, failed login attempts, and new user signups, you can build a robust monitoring system. This post will guide you through the process\u2026","rel":"","context":"In &quot;Spring Events&quot;","block_context":{"text":"Spring Events","link":"https:\/\/www.mymiller.name\/wordpress\/category\/spring_events\/"},"img":{"alt_text":"","src":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/05\/technology-6701509_1280-1.avif","width":350,"height":200,"srcset":"https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/05\/technology-6701509_1280-1.avif 1x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/05\/technology-6701509_1280-1.avif 1.5x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/05\/technology-6701509_1280-1.avif 2x, https:\/\/www.mymiller.name\/wordpress\/wp-content\/uploads\/2025\/05\/technology-6701509_1280-1.avif 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1911","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/comments?post=1911"}],"version-history":[{"count":10,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1911\/revisions"}],"predecessor-version":[{"id":2917,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/posts\/1911\/revisions\/2917"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media\/1902"}],"wp:attachment":[{"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/media?parent=1911"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/categories?post=1911"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/tags?post=1911"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.mymiller.name\/wordpress\/wp-json\/wp\/v2\/series?post=1911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}