{"id":146,"date":"2011-10-27T13:12:49","date_gmt":"2011-10-27T05:12:49","guid":{"rendered":"https:\/\/www.techcoil.com\/blog\/?p=146"},"modified":"2018-09-03T21:09:12","modified_gmt":"2018-09-03T13:09:12","slug":"a-mechanism-for-serving-http-requests-in-c","status":"publish","type":"post","link":"https:\/\/www.techcoil.com\/blog\/a-mechanism-for-serving-http-requests-in-c\/","title":{"rendered":"A mechanism for serving HTTP requests in C#"},"content":{"rendered":"<p>This is part 4 of the sequel to \"<a title=\"Link to how to build a web based user interaction layer in C#\" href=\"http:\/\/www.techcoil.com\/blog\/?p=136\" target=\"_blank\">How to build a web based user interaction layer in C#<\/a>\". In this post, I will describe a mechanism that we can use to locate resources to process HTTP requests in C#.<\/p>\n<p>In part 1, we learn how to receive HTTP requests from clients in our C# program using the <a title=\"MSDN reference to the System.Net.HttpListener class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.net.httplistener.aspx\" target=\"_blank\">System.Net.HttpListener<\/a>.<\/p>\n<p>In part 2, we learn <a title=\"Link to The HTTP request and how it relates to System.Net.HttpListener\" href=\"http:\/\/www.techcoil.com\/blog\/?p=143\"> how we can process the HTTP request using the facilities provided by the <code>System.Net.HttpListenerRequest<\/code> class<\/a>.<\/p>\n<p>In part 3, we learn <a title=\"Link to The HTTP response and how it relates to System.Net.HttpListener\" href=\"http:\/\/www.techcoil.com\/blog\/?p=145\">how we can prepare a HTTP response using the facilities provided by the <code>System.Net.HttpListenerResponse<\/code> class to send back to the client<\/a>.<\/p>\n<h3>Visualize the needed components in the object oriented way<\/h3>\n<p>We can identify three main components which will work together to serve HTTP requests from the client.<\/p>\n<p><img decoding=\"async\" src=\"\/ph\/img\/blog\/posts\/mechanism-to-serve-http-request-in-c-sharp.gif\" alt=\"\" \/><\/p>\n<h4>1) The HTTP server<\/h4>\n<p>We can use the <code>HttpListener<\/code> to build our HTTP server. The HTTP server will run its own thread and serve as the role of listening for the HTTP request. Whenever a <code>HttpListenerContext<\/code> instance is received, it will call the resource locator to find the corresponding request handler to serve the HTTP request.<\/p>\n<h4>2) The resource locator<\/h4>\n<p>The resource locator will examine the HTTP request to determine the resource that the client wish to submit the HTTP request to. If the resource cannot be found, the resource locator response with a 404. If the resource is found, the resource locator create a new thread and call the corresponding request handler to process the HTTP request.<\/p>\n<h4>3) The request handlers<\/h4>\n<p>The request handlers are instances of the <code>HttpRequestHandler<\/code> interface. They will process HTTP requests that are directed to them and create the corresponding HTTP response back to the client.<\/p>\n<h3>A code example<\/h3>\n<p>Suppose we want to create a C# program that can greet the user through the browser.<\/p>\n<p>When I type the url <strong>http:\/\/localhost:12345\/Morning?name=Clivant<\/strong> in my browser address bar, I will see the message <strong>Good morning Clivant!<\/strong>.<\/p>\n<p>When I type the url <strong>http:\/\/localhost:12345\/Afternoon?name=Clivant<\/strong> in my browser address bar, I will see the message <strong>Good afternoon Clivant!<\/strong>.<\/p>\n<p>When I left out my name, I will see the message <strong>Good morning stranger!<\/strong> and <strong>Good afternoon stranger!<\/strong> respectively.<\/p>\n<p>When I type any other url, I will see the message <strong>Could not locate HTTP resource.<\/strong> or the ugly 404 page when my browser is an Internet Explorer.<\/p>\n<h4>The <code>HttpRequestHandler<\/code> interface<\/h4>\n<p>We first define the <code>HttpRequestHandler<\/code> interface to serve as a contract between the resource locator and the request handlers.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic interface HttpRequestHandler\r\n{\r\n    void Handle(HttpListenerContext context);\r\n\r\n    string GetName();\r\n\r\n} \/\/ end public interface HttpRequestHandler\r\n<\/pre>\n<p>The <code>Handle<\/code> method will allow the request handlers to examine the HTTP request and respond to the client. The <code>GetName<\/code> method returns the name of the request handler so that the <code>HttpResourceLocator<\/code> can locate it.<\/p>\n<h4>Three implementations of the <code>HttpRequestHandler<\/code> interface<\/h4>\n<p>This one is for the cases when the resource locator cannot find the a request handler to handle a HTTP request.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System.Net;\r\nusing System.Text;\r\n\r\npublic class InvalidHttpRequestHandler : HttpRequestHandler\r\n{\r\n    public const string NAME = &quot;\/InvalidWebRequestHandler&quot;;\r\n\r\n    public void Handle(HttpListenerContext context)\r\n    {\r\n        HttpListenerResponse serverResponse = context.Response;\r\n\r\n        \/\/ Indicate the failure as a 404 not found\r\n        serverResponse.StatusCode = (int) HttpStatusCode.NotFound;\r\n\r\n        \/\/ Fill in the response body\r\n        string message = &quot;Could not find resource.&quot;;\r\n        byte&#x5B;] messageBytes = Encoding.Default.GetBytes(message);\r\n        serverResponse.OutputStream.Write(messageBytes, 0, messageBytes.Length);\r\n\r\n        \/\/ Send the HTTP response to the client\r\n        serverResponse.Close();\r\n\r\n        \/\/ Print a message to console indicate invalid request as well\r\n        Console.WriteLine(&quot;Invalid request from client. Request string: &quot;\r\n            + context.Request.RawUrl);\r\n    } \/\/ end public void handle(HttpListenerContext context)\r\n\r\n    public string GetName()\r\n    {\r\n        return NAME;\r\n    } \/\/ end public string GetName()\r\n\r\n} \/\/ end public class InvalidHttpRequestHandler\r\n<\/pre>\n<p>This one is for greeting good mornings.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System.Net;\r\nusing System.Text;\r\n\r\npublic class MorningHttpRequestHandler : HttpRequestHandler\r\n{\r\n    public const string NAME = &quot;\/Morning&quot;;\r\n\r\n    public void Handle(HttpListenerContext context)\r\n    {\r\n\r\n        HttpListenerResponse response = context.Response;\r\n        response.StatusCode = (int)HttpStatusCode.OK;\r\n\r\n        \/\/ Get name from query string\r\n        string name = context.Request.QueryString&#x5B;&quot;name&quot;];\r\n        string message;\r\n        if (name == null)\r\n        {\r\n            message = &quot;Good morning stranger!&quot;;\r\n        }\r\n        else\r\n        {\r\n            message = &quot;Good morning &quot; + name + &quot;!&quot;;\r\n        } \/\/ end if\r\n\r\n        \/\/ Fill in response body\r\n        byte&#x5B;] messageBytes = Encoding.Default.GetBytes(message);\r\n        response.OutputStream.Write(messageBytes, 0, messageBytes.Length);\r\n        \/\/ Send the HTTP response to the client\r\n        response.Close();\r\n\r\n    } \/\/ end public void Handle(HttpListenerContext context)\r\n\r\n    public string GetName()\r\n    {\r\n        return NAME;\r\n    }\r\n} \/\/ end public class MorningHttpRequestHandler\r\n<\/pre>\n<p>And this one is for greeting good afternoons.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System.Net;\r\nusing System.Text;\r\n\r\npublic class AfternoonHttpRequestHandler : HttpRequestHandler\r\n{\r\n    public const string NAME = &quot;\/Afternoon&quot;;\r\n\r\n    public void Handle(HttpListenerContext context)\r\n    {\r\n\r\n        HttpListenerResponse response = context.Response;\r\n        response.StatusCode = (int)HttpStatusCode.OK;\r\n\r\n        \/\/ Get name from query string\r\n        string name = context.Request.QueryString&#x5B;&quot;name&quot;];\r\n        string message;\r\n        if (name == null)\r\n        {\r\n            message = &quot;Good afternoon stranger!&quot;;\r\n        }\r\n        else\r\n        {\r\n            message = &quot;Good afternoon &quot; + name + &quot;!&quot;;\r\n        } \/\/ end if\r\n\r\n        \/\/ Fill in response body\r\n        byte&#x5B;] messageBytes = Encoding.Default.GetBytes(message);\r\n        response.OutputStream.Write(messageBytes, 0, messageBytes.Length);\r\n        \/\/ Send the HTTP response to the client\r\n        response.Close();\r\n\r\n    } \/\/ end public void Handle(HttpListenerContext context)\r\n\r\n    public string GetName()\r\n    {\r\n        return NAME;\r\n    } \/\/ end public string GetName()\r\n\r\n} \/\/ end public class AfternoonHttpRequestHandler\r\n<\/pre>\n<h4>The resource locator<\/h4>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System.Collections.Generic;\r\nusing System.Net;\r\nusing System.Threading;\r\n\r\npublic class HttpResourceLocator\r\n{\r\n    private Dictionary&lt;string, HttpRequestHandler&gt; _httpRequestHandlers;\r\n\r\n    public HttpResourceLocator()\r\n    {\r\n        _httpRequestHandlers = new Dictionary&lt;string, HttpRequestHandler&gt;();\r\n        \/\/ Add the default handler that will handle invalid web request\r\n        this.AddHttpRequestHandler(new InvalidHttpRequestHandler());\r\n\r\n    } \/\/ end private HttpRequestController()\r\n\r\n    public void AddHttpRequestHandler(HttpRequestHandler httpRequestHandler)\r\n    {\r\n        \/\/ If the httpRequestHandler is not yet added\r\n        if (!_httpRequestHandlers.ContainsKey(httpRequestHandler.GetName()))\r\n        {\r\n            \/\/ Add a new record\r\n            _httpRequestHandlers.Add(httpRequestHandler.GetName(), httpRequestHandler);\r\n        }\r\n        else\r\n        {\r\n            \/\/ Replace it\r\n            _httpRequestHandlers&#x5B;httpRequestHandler.GetName()] = httpRequestHandler;\r\n        }\r\n    } \/\/ end public void AddHttpRequestHandler(HttpRequestHandler httpRequestHandler)\r\n\r\n    public void HandleContext(HttpListenerContext listenerContext) {\r\n\r\n        \/\/ Search for the requested handler\r\n        HttpListenerRequest request = listenerContext.Request;\r\n        \/\/ Use the absolute path of the url to find the request\r\n        \/\/ handler\r\n        string requestHandlerName = request.Url.AbsolutePath;\r\n\r\n        \/\/ Find the request handler to handle the request\r\n\r\n        HttpRequestHandler handler;\r\n        \/\/ If request handler is found\r\n        if (_httpRequestHandlers.ContainsKey(requestHandlerName))\r\n        {\r\n            \/\/ Get the corresponding request handler\r\n            handler = _httpRequestHandlers&#x5B;requestHandlerName];\r\n        }\r\n        else {\r\n            \/\/ Use the InvalidHttpRequestHandler to handle the request\r\n            handler = _httpRequestHandlers&#x5B;InvalidHttpRequestHandler.NAME];\r\n        } \/\/ end if\r\n\r\n        this.InvokeHandler(handler, listenerContext);\r\n\r\n    } \/\/ end public void handleContext(HttpListenerContext listenerContext)\r\n\r\n    private void InvokeHandler(HttpRequestHandler handler,\r\n        HttpListenerContext context)\r\n    {\r\n        \/\/ Start a new thread to invoke the handler to process the HTTP request\r\n        HandleHttpRequestCommand handleHttpRequestCommand\r\n            = new HandleHttpRequestCommand(handler, context);\r\n        Thread handleHttpRequestThread = new Thread(handleHttpRequestCommand.Execute);\r\n        handleHttpRequestThread.Start();\r\n    } \/\/ end private void InvokeHandler(HttpRequestHandler handler,\r\n      \/\/         HttpListenerContext context)\r\n\r\n    \/\/ Helper class for invoking handler to process\r\n    \/\/ HTTP request\r\n    public class HandleHttpRequestCommand\r\n    {\r\n        private HttpRequestHandler _handler;\r\n        private HttpListenerContext _context;\r\n\r\n        public HandleHttpRequestCommand(HttpRequestHandler handler,\r\n            HttpListenerContext context)\r\n        {\r\n            this._handler = handler;\r\n            this._context = context;\r\n        }\r\n\r\n        public void Execute()\r\n        {\r\n            this._handler.Handle(this._context);\r\n        }\r\n    } \/\/ end public class HandleHttpRequestCommand\r\n\r\n} \/\/ end public class HttpResourceLocator\r\n<\/pre>\n<p>The resource locator utilises a <a title=\"MSDN reference for the System.Collections.Generic.Dictionary class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/xfhwa508.aspx\" target=\"_blank\"><code>System.Collections.Generic.Dictionary<\/code><\/a> instance to contain the different request handlers, which can be added via the <code>AddHttpRequestHandler<\/code> method. When a HTTP request arrives via the <code>HandleContext<\/code> method, the resource locator will invoke the corresponding request handler in a new thread.<\/p>\n<h4>The HTTP server<\/h4>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class HttpServer : IDisposable\r\n{\r\n    private HttpListener _httpListener = null;\r\n    private Thread _connectionThread = null;\r\n    private Boolean _running, _disposed;\r\n\r\n    private HttpResourceLocator _resourceLocator = null;\r\n\r\n    public HttpServer(string prefix)\r\n    {\r\n        if (!HttpListener.IsSupported) {\r\n            \/\/ Requires at least a Windows XP with Service Pack 2\r\n            throw new NotSupportedException(\r\n                &quot;The Http Server cannot run on this operating system.&quot;);\r\n        } \/\/ end if HttpListener is not supported\r\n\r\n        _httpListener = new HttpListener();\r\n        \/\/ Add the prefixes to listen to\r\n        _httpListener.Prefixes.Add(prefix);\r\n\r\n        _resourceLocator = new HttpResourceLocator();\r\n\r\n    } \/\/ end WebServer()\r\n\r\n    public void AddHttpRequestHandler(HttpRequestHandler requestHandler)\r\n    {\r\n        _resourceLocator.AddHttpRequestHandler(requestHandler);\r\n    }\r\n\r\n    public void Start()\r\n    {\r\n        if (!_httpListener.IsListening)\r\n        {\r\n            _httpListener.Start();\r\n            _running = true;\r\n            \/\/ Use a thread to listen to the Http requests\r\n            _connectionThread = new Thread(new ThreadStart(this.ConnectionThreadStart));\r\n            _connectionThread.Start();\r\n        } \/\/ end if httpListener is not listening\r\n\r\n    } \/\/ end public void start()\r\n\r\n    public void Stop()\r\n    {\r\n        if (_httpListener.IsListening)\r\n        {\r\n            _running = false;\r\n            _httpListener.Stop();\r\n        } \/\/ end if httpListener is listening\r\n    } \/\/ end public void stop()\r\n\r\n    \/\/ Action body for _connectionThread\r\n    private void ConnectionThreadStart()\r\n    {\r\n        try\r\n        {\r\n            while (_running)\r\n            {\r\n                \/\/ Grab the context and pass it to the HttpResourceLocator to handle it\r\n                HttpListenerContext context = _httpListener.GetContext();\r\n                _resourceLocator.HandleContext(context);\r\n\r\n            } \/\/ while running\r\n        }\r\n        catch (HttpListenerException)\r\n        {\r\n            \/\/ This will occurs when the listener gets shutdown.\r\n            Console.WriteLine(&quot;HTTP server was shut down.&quot;);\r\n        } \/\/ end try-catch\r\n\r\n    } \/\/ end private void connectionThreadStart()\r\n\r\n    public virtual void Dispose()\r\n    {\r\n        this.Dispose(true);\r\n        GC.SuppressFinalize(this);\r\n    } \/\/ end public virtual void Dispose()\r\n\r\n    private void Dispose(bool disposing)\r\n    {\r\n        if (this._disposed)\r\n        {\r\n            return;\r\n        }\r\n        if (disposing)\r\n        {\r\n            if (this._running)\r\n            {\r\n                this.Stop();\r\n            }\r\n            if (this._connectionThread != null)\r\n            {\r\n                this._connectionThread.Abort();\r\n                this._connectionThread = null;\r\n            }\r\n        }\r\n        this._disposed = true;\r\n    } \/\/ private void Dispose(bool disposing)\r\n\r\n} \/\/ end class HttpServer\r\n<\/pre>\n<p>The HttpServer accepts one Uniform Resource Identifier (URI) prefix when instantiated. It utilises an instance of the System.Net.HttpListener class internally to listen for HTTP requests that clients send to the URI prefix. Whenever a HTTP request is received, it delegates the handling of the HTTP request to the resource locator. It provides a AddHttpRequestHandler method that will add an instance of the HttpRequestHandler to the resource locator.<\/p>\n<h4>A executable implementation<\/h4>\n<p>The Program class concludes our code example with the creation of an executable that listens for HTTP requests that is send to port 12345.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System;\r\n\r\npublic class Program\r\n{\r\n    public static void Main(string&#x5B;] args)\r\n    {\r\n\r\n        HttpServer server = new HttpServer(&quot;http:\/\/*:12345\/&quot;);\r\n        \/\/ Add the HttpRequestHandlers\r\n        server.AddHttpRequestHandler(new MorningHttpRequestHandler());\r\n        server.AddHttpRequestHandler(new AfternoonHttpRequestHandler());\r\n        \/\/ Start the server\r\n        server.Start();\r\n        Console.ReadKey();\r\n\r\n    } \/\/ end\r\n} \/\/ end public class Program\r\n<\/pre>\n<h3>Go build your own web based interaction layer in C#<\/h3>\n<p>At this point, I hope I had covered enough details to aid you in building your own web based interaction layer in C#. There are areas which I did not cover, in particular, security of the web based interaction layer. If you need security, I strongly recommend IIS, Apache or any other web servers that are time-tested.<\/p>\n<h3>Related posts<\/h3>\n<p>The following is a list of posts that relates to sending HTTP requests to web servers. Feel free to look through them as well. \ud83d\ude42<\/p>\n<ul>\n<li><a href=\"http:\/\/www.techcoil.com\/blog\/quick-references\/downloading-a-file-from-via-http-post-and-http-get-in-c\/\" target=\"_blank\">Downloading a file via HTTP post and HTTP get in C#<\/a><\/li>\n<li><a href=\"http:\/\/www.techcoil.com\/blog\/quick-references\/sending-a-file-and-some-form-data-via-http-post-in-c\/\" target=\"_blank\">Sending a file and some form data via HTTP post in C#<\/a><\/li>\n<li><a href=\"http:\/\/www.techcoil.com\/blog\/quick-references\/uploading-large-http-multipart-request-with-system-net-httpwebrequest-in-c\/\" target=\"_blank\">Uploading large HTTP multipart request with <code>System.Net.HttpWebRequest<\/code> in C#<\/a><\/li>\n<li><a href=\"http:\/\/www.techcoil.com\/blog\/quick-references\/handling-web-server-communication-feedback-with-system-net-webexception\/\" target=\"_blank\">Handling web server communication feedback with <code>System.Net.WebException<\/code> in C#<\/a><\/li>\n<\/ul>\n<h3>Need data persistence for your C# web server?<\/h3>\n<p>You could probably look at <a href=\"http:\/\/www.techcoil.com\/blog\/quick-references\/my-experience-with-system-data-sqlite-in-c\/\" target=\"_blank\">my experience with System.Data.SQLite in C#<\/a>.<\/p>\n\n      <ul id=\"social-sharing-buttons-list\">\n        <li class=\"facebook\">\n          <a href=\"https:\/\/www.facebook.com\/sharer\/sharer.php?u=https%3A%2F%2Fwp.me%2Fp245TQ-2m\" target=\"_blank\" role=\"button\" rel=\"nofollow\">\n            <img decoding=\"async\" src=\"\/ph\/img\/3rd-party\/social-icons\/Facebook.png\" alt=\"Facebook icon\"> Share\n          <\/a>\n        <\/li>\n        <li class=\"twitter\">\n          <a href=\"https:\/\/twitter.com\/intent\/tweet?text=&url=https%3A%2F%2Fwp.me%2Fp245TQ-2m&via=Techcoil_com\" target=\"_blank\" role=\"button\" rel=\"nofollow\">\n          <img decoding=\"async\" src=\"\/ph\/img\/3rd-party\/social-icons\/Twitter.png\" alt=\"Twitter icon\"> Tweet\n          <\/a>\n        <\/li>\n        <li class=\"linkedin\">\n          <a href=\"https:\/\/www.linkedin.com\/shareArticle?mini=1&title=&url=https%3A%2F%2Fwp.me%2Fp245TQ-2m&source=https:\/\/www.techcoil.com\" target=\"_blank\" role=\"button\" rel=\"nofollow\">\n          <img decoding=\"async\" src=\"\/ph\/img\/3rd-party\/social-icons\/linkedin.png\" alt=\"Linkedin icon\"> Share\n          <\/a>\n        <\/li>\n        <li class=\"pinterest\">\n          <a href=\"https:\/\/pinterest.com\/pin\/create\/button\/?url=https%3A%2F%2Fwww.techcoil.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F146&description=\" class=\"pin-it-button\" target=\"_blank\" role=\"button\" rel=\"nofollow\" count-layout=\"horizontal\">\n          <img decoding=\"async\" src=\"\/ph\/img\/3rd-party\/social-icons\/Pinterest.png\" alt=\"Pinterest icon\"> Save\n          <\/a>\n        <\/li>\n      <\/ul>\n    ","protected":false},"excerpt":{"rendered":"<p>This is part 4 of the sequel to &#8220;<a href=\"http:\/\/www.techcoil.com\/blog\/?p=136\" target = \"_blank\" title=\"Link to how to build a web based user interaction layer in C#\">How to build a web based user interaction layer in C#<\/a>&#8220;. In this post, I will describe a mechanism that we can use to locate resources to process HTTP requests in C#.<\/p>\n","protected":false},"author":1,"featured_media":1189,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"footnotes":""},"categories":[375],"tags":[20,23,56,63,58,64],"jetpack_featured_media_url":"https:\/\/www.techcoil.com\/blog\/wp-content\/uploads\/C-Sharp-Logo.gif","jetpack_shortlink":"https:\/\/wp.me\/p245TQ-2m","jetpack-related-posts":[],"jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/posts\/146"}],"collection":[{"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":0,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/media\/1189"}],"wp:attachment":[{"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.techcoil.com\/blog\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}