Cyotek Development Bloghttps://devblog.cyotek.com/tag/asp-net/atom.xml2017-11-19T17:12:25ZRedirecting to HTTPS when using IIS behind a load balancerurn:uuid:8ceab7e1-834d-4f00-bc82-c78c6bcbaa6a2017-11-19T17:12:25Z2017-11-19T13:25:52Z<p>Cyotek has a number of different websites powering various bits
of our software and services. Some are Windows based using IIS,
and some are Linux based using Apache. Internal servers are are
directly accessed but external servers are behind load
balancers. Almost all are using HTTPS now and have redirects in
place to force the use of HTTPS over HTTP.</p>
<p>There are plenty of articles on the internet dealing how to use
<code>.htaccess</code> files to perform redirects using Apache, and various
articles on different methods of redirecting IIS or ASP.NET
applications. However, there seems to be a slight gap when it
comes to load balancers or reverse proxies. Depending on how the
load balancer / reverse proxy (referred to as just load balancer
for the rest of the article) operates, the secure connection may
terminate at the load balancer, and so the web server always
receives &quot;insecure&quot; traffic.</p>
<p>If the secure connection is terminated at the balancer, then
this can be a problem for the most common method of redirecting
traffic, which is to check if the current request's scheme is
HTTP and perform a redirect if it is. But if the web server
<em>always</em> gets HTTP traffic, then this approach will simply
result in an infinite redirect loop, something I've managed to
do inadvertently more than once.</p>
<p>Fortunately however, when a load balancer forwards traffic to
the final server, it generally includes some extra headers such
as <code>X-Forwarded-For</code> and <code>X-Forwarded-Proto</code>. These headers
provide details such as the original scheme/protocol being used
and the origin IP address.</p>
<blockquote>
<p>When researching the final details for writing this article, I
found that the non-standard <code>X-Forwarded-*</code> headers have been
replaced under <a href="https://tools.ietf.org/html/rfc7239" rel="external nofollow noopener">RFC 7239</a> by a new <code>Forwarded</code> header. It's
possible therefore that the <code>X-Forwarded-For</code> header may start
dropping out of use and you'd be expected to work with
<code>Forwarded</code> instead.</p>
</blockquote>
<p>The header I'm interested in in this case is <code>X-Forwarded-Proto</code>
which simply details the scheme of the original request. If the
value is <code>http</code>, then clearly the original request wasn't secure
and so I can issue a redirect. If it's <code>https</code>, then the
original request - up to hitting the load balancer - was secure,
and we shouldn't try and redirect, or we're back into infinite
loop territory.</p>
<blockquote>
<p>Remember that these are request headers, e.g. headers you
can't control and which could be forged or manipulated.</p>
</blockquote>
<p>When using IIS, I've mostly used code based redirects although
the very oldest bits use an ancient configuration based
re-writer named <code>Intelligencia.UrlRewriter</code>. However, for some
time IIS now has had an optional URL rewriter of its own, which
is what I'm going to use in this article.</p>
<blockquote>
<p>By default the URL rewrite module isn't installed, although
it's easy enough to add using the Web Platform Installer. I've
detailed the steps in a <a href="/post/installing-the-url-rewrite-module-into-internet-information-services">prior
post</a>.</p>
</blockquote>
<h2 id="adding-a-rule-to-force-https">Adding a rule to force HTTPS</h2>
<p>Although you can configure rules from within IIS Managers GUI
interface, I'm just going to detail how to directly modify the
<code>web.config</code> of an application.</p>
<p>In your <code>web.config</code>, find the <code>system.webServer</code> element (or
add it as a child of <code>configuration</code> if it is not already
present), and add a <code>rewrite</code> element containing the following
content.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">rewrite</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rules</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- Specify a rule element. The stopProcessing attribute means no other
 rules will be processed if this rule is a match. You can also add a boolean
 attribute named enabled which you can use to temporarily disable a rule
 without deleting it from the file --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">rule</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">HTTPS Redirect</span><span class="symbol">&quot;</span> <span class="name">stopProcessing</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- Specifies a regular expression used to match the incoming URL. As I
 want all URL&#39;s to be considered, I tell it to match any character. By wrapping 
 the expression in brackets I create a capturing group I can use elsewhere. --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">match</span> <span class="name">url</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">(.*)</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="comment">&lt;!-- Each rule can have one or more conditions --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">conditions</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- Here I&#39;ve added a condition which looks at the X-Forwarded-Proto
 header to see if it&#39;s any value other than &quot;https&quot;. (The attribute
 negate=&quot;true&quot; means &quot;does not match&quot;) --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">add</span> <span class="name">input</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{HTTP_X_FORWARDED_PROTO}</span><span class="symbol">&quot;</span> <span class="name">pattern</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">https</span><span class="symbol">&quot;</span> <span class="name">negate</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">conditions</span><span class="symbol">&gt;</span>
 <span class="comment">&lt;!-- The action element states what should happen if all the rule
 conditions are met. This action instructs IIS to perform a 301 redirect
 to the original host (via the HTTP_HOST variable) with the original request
 path (as captured in the match element above) via R:1 --&gt;</span>
 <span class="symbol">&lt;</span><span class="name">action</span> <span class="name">type</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Redirect</span><span class="symbol">&quot;</span> <span class="name">url</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">https://{HTTP_HOST}/{R:1}</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">rule</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">rules</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">rewrite</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Rather than trying to add a description outside the code block,
I've added a bunch of comments inside the XML.</p>
<p>Happily, these are the only changes you need to make to
web.config files - you don't need to define new
<code>configSections</code>, <code>httpHandlers</code>, or other elements. I assume
these have been pre-defined at a higher level, but haven't
confirmed this.</p>
<h2 id="a-note-on-using-letsencrypt-renewal-challenges">A note on using LetsEncrypt renewal challenges</h2>
<p>If you are renewing LetsEncrypt certificates via a HTTP
challenge (where a file is placed on your server to confirm
validity), then this rule will cause the challenge to fail due
to the redirect. If your processes are set to only renew the
certificates at the last minute then SSL can stop working on
your website, causing web browsers to display unwelcome danger
messages when visitors attempt to access your site. Somethign
<em>else</em> that has happened more than once to me, and the main
reason why cyotek.com is now running off a 3 year traditional
certificate after lasts months mishap.</p>
<p>As the file is always in a static location
(<code>.well-known/acme-challenge</code>) we can expand our original rule
to add a new condition which prevents the redirect from
triggering if that path is present.</p>
<p>Using the example rule above, I added a new child of the
<code>conditions</code> element as follows.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">add</span> <span class="name">input</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{PATH_INFO}</span><span class="symbol">&quot;</span> <span class="name">pattern</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">^/\.well-known/acme-challenge/.*</span><span class="symbol">&quot;</span> <span class="name">negate</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">true</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
</pre>
</figure>
<p>When testing a HTML located in this path, the request was not
forcibly redirected to HTTPS and was displayed via unsecure
HTTP, which should allow LetsEncrypt challenges to succeed. Of
course, the certificates on the domains where I've implemented
these rules aren't due to expire for another two months so time
will tell if this is sufficient.</p>
<h2 id="closing-notes">Closing notes</h2>
<p>Forcing a redirect from HTTP to HTTPS is only one part - you
should also consider using a Content Security Policy (CSP) that
instructs browsers to automatically use HTTPS and setting up
HTTP Strict Transport Security (HSTS). You should also ensure
the redirects you put in place are 301 to guide search engines
to update references (the example above uses a 301 redirect).
Lots of things to try and remember! <a href="https://www.troyhunt.com/" rel="external nofollow noopener">Troy Hunt</a> has a helpful
post on <a href="https://www.troyhunt.com/the-6-step-happy-path-to-https/" rel="external nofollow noopener">getting started with HTTPS</a>.</p>
<p>There is also lots more you can do using rewrite rules - you can
view the <a href="https://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference?amp;clcid=0x409" rel="external nofollow noopener">documentation</a> for more information and examples.
And, although in this article I only covered modifying
<code>web.config</code> directly, you can use the GUI tools in IIS Manager
to explore all the supported function and experiment with rule
creation.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2017-11-19 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/redirecting-to-https-when-using-iis-behind-a-load-balancer .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comDisplaying the contents of a PDF file in an ASP.NET application using GhostScripturn:uuid:808da458-9ecb-459e-8797-0c9f80a1be6e2012-07-10T19:06:46Z2012-07-10T19:06:46Z<p>After receiving quite a few requests on making the <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">PDF image
conversion</a> work in a web application, I wanted to see how
hard it would be to do. Not hard at all as it turns out, I had a
nice working sample running with a bare 5 minutes of work.</p>
<p>The sample available for download below is a basic ASP.NET
application, comprised of a single page with an <code>IHttpHandler</code>
for displaying the image. In order to make this sample as easy
as possible, it uses pure server side controls and code, nothing
client side.</p>
<h2 id="getting-started">Getting Started</h2>
<p>In order to run this sample, you'll need the
<a href="https://www.cyotek.com/downloads/view/Cyotek.GhostScript.zip/"><code>Cyotek.GhostScript</code></a> and
<a href="https://www.cyotek.com/downloads/view/Cyotek.GhostScript.PdfConversion.zip/"><code>Cyotek.GhostScript.PdfConversion.zip</code></a> components described
in a <a href="/post/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript">previous article</a>.</p>
<p>You'll also need to download <a href="http://www.ghostscript.com/" rel="external nofollow noopener">GhostScript</a>. As with my other
articles on the subject, please make sure you check their
license terms - they seem very keen that people don't use the
GPL version or distribute GhostScript without a commercial
license.</p>
<h2 id="locating-gsdll32.dll">Locating gsdll32.dll</h2>
<p>In order for this to work, <code>gsdll32.dll</code> needs to be somewhere
in your applications path. This could be in your <code>system32</code>
directory on 32bit Windows, or <code>SysWOW64</code> on 64bit Windows.</p>
<p>While developing this sample, I also tried having the file in
the bin directory of the website - this also worked fine.
However, as the website was running on my local machine, it's
probably running in Full Trust, and I have no idea if it will
work in Medium Trust or lower.</p>
<h3 id="im-running-64bit-windows">I'm running 64bit Windows</h3>
<p>Congratulations! I have nothing but issues with 32bit web
servers. But I digress. The sample projects I have provided on
this website all use the 32bit version of GhostScript. There is
a 64bit version available, but I haven't downloaded it to test.
Your options should be as follows:</p>
<ul>
<li>Build against the 64bit GhostScript DLL. This may need some
refactoring if their public API has changed. At the very
least, you'll need to change the DLL filename in the native
method calls.</li>
<li>Using IIS7 or higher? Keep using the 32bit version, and set
your worker pool to run in 32bit mode</li>
<li>Using IIS6? Commiserations, I feel your pain. The only option
here, if you stay 32bit, is to have the entire IIS run as
32bit.</li>
</ul>
<p>I have tested on a Windows 7 Professional 64bit machine as
follows:</p>
<ul>
<li>Firstly, using IISExpress which is running as a 32bit process</li>
<li>Secondly, using IIS7 with a custom application pool running in
32bit mode</li>
</ul>
<p>Both of these scenarios worked perfectly well.</p>
<h2 id="creating-the-solution">Creating the solution</h2>
<p>Create a new <strong>ASP.NET Web Forms Site</strong></p>
<blockquote>
<p>Note: Even though this example uses pure WebForms, there's no
reason that this sort of code won't work fine in ASP.NET MVC
or any other .NET framework of your choice.</p>
</blockquote>
<p>Open up <code>Default.aspx</code> and add some controls similar to the
following:</p>
<figure class="lang-html highlight"><figcaption><span>html</span></figcaption><pre class="code">
<span class="htmlServerSideScript">&lt;%</span><span class="literal">@</span> <span class="name">Page</span> <span class="name">Language</span><span class="symbol">=</span><span class="attribute">&quot;C#&quot;</span> <span class="name">AutoEventWireup</span><span class="symbol">=</span><span class="attribute">&quot;true&quot;</span> <span class="name">CodeBehind</span><span class="symbol">=</span><span class="attribute">&quot;Default.aspx.cs&quot;</span> <span class="name">Inherits</span><span class="symbol">=</span><span class="attribute">&quot;GhostScriptWebTest._Default&quot;</span> <span class="htmlServerSideScript">%&gt;</span>

<span class="literal">&lt;!</span><span class="name">DOCTYPE</span> <span class="name">html</span> <span class="name">PUBLIC</span> <span class="attribute">&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span> <span class="attribute">&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;</span><span class="literal">&gt;</span>

<span class="literal">&lt;</span><span class="name">html</span> <span class="name">xmlns</span><span class="symbol">=</span><span class="attribute">&quot;http://www.w3.org/1999/xhtml&quot;</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">head</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">title</span><span class="literal">&gt;</span>PDF Conversion Example<span class="literal">&lt;/</span><span class="name">title</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">head</span><span class="literal">&gt;</span>
<span class="literal">&lt;</span><span class="name">body</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">form</span> <span class="name">id</span><span class="symbol">=</span><span class="attribute">&quot;form1&quot;</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">div</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">LinkButton</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;previousLinkButton&quot;</span> <span class="name">Text</span><span class="symbol">=</span><span class="attribute">&quot;Previous&quot;</span> <span class="name">OnClick</span><span class="symbol">=</span><span class="attribute">&quot;previousLinkButton_Click&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">LinkButton</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;nextLinkButton&quot;</span> <span class="name">Text</span><span class="symbol">=</span><span class="attribute">&quot;Next&quot;</span> <span class="name">OnClick</span><span class="symbol">=</span><span class="attribute">&quot;nextLinkButton_Click&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;</span><span class="name">asp</span><span class="literal">:</span><span class="name">Image</span> <span class="name">runat</span><span class="symbol">=</span><span class="attribute">&quot;server&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="attribute">&quot;pdfImage&quot;</span> <span class="name">ImageUrl</span><span class="symbol">=</span><span class="attribute">&quot;~/PdfImage.ashx?fileName=sample.pdf&amp;page=1&quot;</span> <span class="literal">/&gt;</span>
 <span class="literal">&lt;/</span><span class="name">p</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">div</span><span class="literal">&gt;</span>
 <span class="literal">&lt;/</span><span class="name">form</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">body</span><span class="literal">&gt;</span>
<span class="literal">&lt;/</span><span class="name">html</span><span class="literal">&gt;</span>
</pre>
</figure>
<p>The controls should be fairly self explanatory! The main thing
of interest is the <code>pdfImage</code> Image control - this will call a
<em>Generic Handler</em> that I'll describe in the next section. Note
that VS2010 and VS2012 have another option, an <strong>ASP.NET
Handler</strong> - this implements the same <code>IHttpHandler</code> interface
but doesn't have a <code>.ashx</code> file and is registered differently.
If you are using IIS7 or above, you're probably better off using
that.</p>
<p>Note that by default the <code>pdfImage</code> control is pointing to a
sample file named <em>sample.pdf</em> - add any old PDF to the root of
your website and name it sample. Ensure that the <strong>Build
Action</strong> for the PDF is set to <strong>Content</strong>, otherwise it won't
be deployed with your application.</p>
<h3 id="creating-the-image-handler">Creating the image handler</h3>
<p>Tutorials on creating image handlers with <code>IHttpHandler</code> can be
found scattered throughout the net, so I'll not go into how they
work, but just describe the implementation I'm using in this
example. Add a new generic handler to your project, then fill in
the <code>ProcessRequest</code> method as follows. Make sure you add the
two GhostScript API components to your solution and add
references to them to your web application first!</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Drawing<span class="symbol">.</span>Imaging<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Web<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>GhostScript<span class="symbol">.</span>PdfConversion<span class="symbol">;</span>

<span class="keyword">namespace</span> GhostScriptWebTest
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">class</span> PdfImage <span class="symbol">:</span> IHttpHandler
 <span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">void</span> ProcessRequest<span class="symbol">(</span>HttpContext context<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> fileName<span class="symbol">;</span>
 <span class="keyword">int</span> pageNumber<span class="symbol">;</span>
 Pdf<span class="number">2</span>Image convertor<span class="symbol">;</span>
 Bitmap image<span class="symbol">;</span>

 fileName <span class="symbol">=</span> context<span class="symbol">.</span>Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/&quot;</span> <span class="symbol">+</span> context<span class="symbol">.</span>Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;fileName&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
 pageNumber <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>context<span class="symbol">.</span>Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;page&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// convert the image</span>
 convertor <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span>fileName<span class="symbol">)</span><span class="symbol">;</span>
 image <span class="symbol">=</span> convertor<span class="symbol">.</span>GetImage<span class="symbol">(</span>pageNumber<span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// set the content type</span>
 context<span class="symbol">.</span>Response<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;image/png&quot;</span><span class="symbol">;</span>

 <span class="comment">// save the image directly to the response stream</span>
 image<span class="symbol">.</span>Save<span class="symbol">(</span>context<span class="symbol">.</span>Response<span class="symbol">.</span>OutputStream<span class="symbol">,</span> ImageFormat<span class="symbol">.</span>Png<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">bool</span> IsReusable
 <span class="symbol">{</span> <span class="keyword">get</span> <span class="symbol">{</span> <span class="keyword">return</span> <span class="keyword">true</span><span class="symbol">;</span> <span class="symbol">}</span> <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Again, this is extremely simple code. I extract the query string
of the request to obtain the file name of the PDF document to
convert, and the page to display. I then create an instance of
the <code>Pdf2Image</code> class, and grab an image of the specified page.</p>
<p>Next, you need to set the <code>ContentType</code> of the <code>Response</code> object
so the web browser knows what to do with your content. Finally,
I save the image directly to the response's <code>OutputStream</code>. Make
sure that the format you save the image as matches the content
type you've specified.</p>
<p>With these steps complete, building and running the website
should present you with a pair of hyper links, and the first
page of your PDF file as a static image. [Well, it will if you
add a pair of blank event handlers for those defined for the two
hyperlink buttons anyway]</p>
<h2 id="simple-navigation">Simple navigation</h2>
<p>Now that we can display our PDF, we'll add some basic
navigation. Open up the code behind file for <code>Default.aspx</code> and
fill in the event handlers for the two hyperlink buttons.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Collections<span class="symbol">.</span>Specialized<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Web<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>GhostScript<span class="symbol">.</span>PdfConversion<span class="symbol">;</span>

<span class="keyword">namespace</span> GhostScriptWebTest
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> _Default <span class="symbol">:</span> System<span class="symbol">.</span>Web<span class="symbol">.</span>UI<span class="symbol">.</span>Page
 <span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">void</span> previousLinkButton_Click<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IncrementPage<span class="symbol">(</span><span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">protected</span> <span class="keyword">void</span> nextLinkButton_Click<span class="symbol">(</span><span class="keyword">object</span> sender<span class="symbol">,</span> EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">this</span><span class="symbol">.</span>IncrementPage<span class="symbol">(</span><span class="number">1</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> IncrementPage<span class="symbol">(</span><span class="keyword">int</span> increment<span class="symbol">)</span>
 <span class="symbol">{</span>
 NameValueCollection queryString<span class="symbol">;</span>
 <span class="keyword">int</span> pageNumber<span class="symbol">;</span>
 <span class="keyword">string</span> pdfFileName<span class="symbol">;</span>
 Pdf<span class="number">2</span>Image converter<span class="symbol">;</span>

 queryString <span class="symbol">=</span> HttpUtility<span class="symbol">.</span>ParseQueryString<span class="symbol">(</span>pdfImage<span class="symbol">.</span>ImageUrl<span class="symbol">.</span>Substring<span class="symbol">(</span>pdfImage<span class="symbol">.</span>ImageUrl<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&quot;?&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 pdfFileName <span class="symbol">=</span> queryString<span class="symbol">[</span><span class="string">&quot;fileName&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 pageNumber <span class="symbol">=</span> Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>queryString<span class="symbol">[</span><span class="string">&quot;page&quot;</span><span class="symbol">]</span><span class="symbol">)</span> <span class="symbol">+</span> increment<span class="symbol">;</span>
 converter <span class="symbol">=</span> <span class="keyword">new</span> Pdf<span class="number">2</span>Image<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">.</span>Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/&quot;</span> <span class="symbol">+</span> pdfFileName<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>pageNumber <span class="symbol">&gt;</span> <span class="number">0</span> <span class="symbol">&amp;&amp;</span> pageNumber <span class="symbol">&lt;=</span> converter<span class="symbol">.</span>PageCount<span class="symbol">)</span>
 pdfImage<span class="symbol">.</span>ImageUrl <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;~/PdfImage.ashx?fileName={0}&amp;page={1}&quot;</span><span class="symbol">,</span> pdfFileName<span class="symbol">,</span> pageNumber<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As with the image handler, this code simply extracts the file
name of the PDF file and the current page number. It also
creates a new instance of the <code>Pdf2Image</code> class in order to
obtain the number of pages in the document. If the new page
number is in range, it updates the <code>ImageUrl</code> of the <code>pdfImage</code>
causing the image handler to pull back the next page.</p>
<h2 id="in-conclusion">In Conclusion</h2>
<p>This sample is pretty inefficient and at the very least should
be caching the images. But, it's as simple an example as I can
make. Hopefully someone will find it useful. At the present time
I'm not working with the GhostScript API library so I suspect
this will be the last article on the subject for the time being.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2012-07-10 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/displaying-the-contents-of-a-pdf-file-in-an-asp-net-application-using-ghostscript .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comCreating a trackback handler using C#urn:uuid:787e3c16-2fe4-4e70-b211-65776f508c0a2010-09-26T19:24:24Z2010-09-22T19:50:32Z<p>Cyotek.com runs on its own custom CMS/blog engine developed in
ASP.NET MVC 1.0, which has a number of advantages and
disadvantages. One of these disadvantages is no automatic
support for some common blog features such as trackbacks and
pingbacks.</p>
<p>This article will describe how to create a trackback handler for
use with MVC and the more traditional webforms.</p>
<h2 id="what-is-a-trackback">What is a trackback?</h2>
<p>A trackback is a way to be notified when a website links to a
resource on your own site. Some blogging software supports
automatic linking, so if a post on that site links to another,
when the post is submitted, it will automatically detect the
link and attempt to send a trackback to the original author. If
successful, a link is generally created from the original author
to the new post, thus building a web of interconnected resources
(in theory). You can learn a little more about trackbacks from
<a href="http://en.wikipedia.org/wiki/Trackback" rel="external nofollow noopener">Wikipedia</a>.</p>
<p>The full trackback specification can be viewed at the <a href="http://www.sixapart.com/pronet/docs/trackback_spec" rel="external nofollow noopener">SixApart
website</a></p>
<h2 id="a-trackback-handler-in-c">A trackback handler in C#</h2>
<p>Unlike pingbacks (which we'll address in a future article),
trackbacks use standard HTTP requests and so are extremely easy
to implement.</p>
<p>Available for download at the end of this article is a sample
library which you can use to implement your trackbacks.</p>
<p>As a trackback is comprised of several pieces of information
which we'll be passing about, we'll start by defining a
structure to hold this information.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">struct</span> TrackbackInfo
<span class="symbol">{</span>
 <span class="keyword">public</span> <span class="keyword">string</span> BlogName <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Excerpt <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Id <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> <span class="keyword">string</span> Title <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>

 <span class="keyword">public</span> Uri Uri <span class="symbol">{</span> <span class="keyword">get</span><span class="symbol">;</span> <span class="keyword">set</span><span class="symbol">;</span> <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The properties of this structure mirror the required information
from the trackback specification.</p>
<p>Next, we'll define an enum for the different result codes you
can return. The specification states 0 for success and 1 for
error, but I'm uncertain if you can extend this, ie is any
non-zero is classed as an error. We'll play it safe and just use
a single error code.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> TrackbackErrorCode
<span class="symbol">{</span>
 Success<span class="symbol">,</span>
 Error
<span class="symbol">}</span>
</pre>
</figure>
<p>I'd considered two ways of implementing this, the first being an
abstract class containing methods which must be implemented in
order to provide the functionality for saving a trackback into
your chosen data source, or using delegates. In order to make it
a simple as possible to use, I've went with the latter.
Therefore, we need two delegates, one which will resolve the
&quot;permalink&quot; for the given ID, and another to actually save the
trackback.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">delegate</span> Uri GetTrackbackUrlDelegate<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">public</span> <span class="keyword">delegate</span> <span class="keyword">void</span> SaveTrackbackDelegate<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="implementing-the-handler">Implementing the handler</h2>
<p>We've created a static class named <code>TrackbackHandler</code> which
contains all the functionality we'll need. We expose a single
public method, <code>GetTrackback</code>, which will return the XML block
required to notify the sender of the result of the request.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackback<span class="symbol">(</span>NameValueCollection form<span class="symbol">,</span> SaveTrackbackDelegate saveTrackbackDelegate<span class="symbol">,</span> GetTrackbackUrlDelegate getTrackbackUrlDelegate<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> url<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>form <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;form&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>saveTrackbackDelegate <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;saveTrackbackDelegate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>getTrackbackUrlDelegate <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException<span class="symbol">(</span><span class="string">&quot;getTrackbackUrlDelegate&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 url <span class="symbol">=</span> form<span class="symbol">[</span><span class="string">&quot;url&quot;</span><span class="symbol">]</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>url<span class="symbol">)</span> <span class="symbol">&amp;&amp;</span> url<span class="symbol">.</span>Contains<span class="symbol">(</span><span class="string">&quot;,&quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 url <span class="symbol">=</span> url<span class="symbol">.</span>Split<span class="symbol">(</span><span class="string">&#39;,&#39;</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">;</span>

 <span class="keyword">return</span> TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>saveTrackbackDelegate<span class="symbol">,</span> getTrackbackUrlDelegate<span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">,</span> url<span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;title&quot;</span><span class="symbol">]</span><span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;excerpt&quot;</span><span class="symbol">]</span><span class="symbol">,</span> form<span class="symbol">[</span><span class="string">&quot;blog_name&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This function accepts the following arguments:</p>
<ul>
<li>A <code>NameValueCollection</code> holding the submitted trackback data -
supporting both the MVC <code>FormCollection</code> or <code>Request.Form</code> for
ASP.NET.</li>
<li>An implementation of the <code>SaveTrackbackDelegate</code> delegate for
saving the trackback to your chosen data store.</li>
<li>An implementation of the <code>GetTrackbackUrlDelegate</code> for
resolving a permalink URL of the given ID.</li>
</ul>
<p>Assuming none of these are null, the method then calls a private
overload, explicitly specifying the individual items of data.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackback<span class="symbol">(</span>SaveTrackbackDelegate saveTrackbackDelegate<span class="symbol">,</span> GetTrackbackUrlDelegate getTrackbackUrlDelegate<span class="symbol">,</span> <span class="keyword">string</span> id<span class="symbol">,</span> <span class="keyword">string</span> url<span class="symbol">,</span> <span class="keyword">string</span> title<span class="symbol">,</span> <span class="keyword">string</span> excerpt<span class="symbol">,</span> <span class="keyword">string</span> blogName<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 HttpRequest request<span class="symbol">;</span>

 request <span class="symbol">=</span> HttpContext<span class="symbol">.</span>Current<span class="symbol">.</span>Request<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>id<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;The entry ID is missing&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span>request<span class="symbol">.</span>HttpMethod <span class="symbol">!=</span> <span class="string">&quot;POST&quot;</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;An invalid request was made.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>url<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;Trackback URI not specified.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>First, we validate that the request is being made via a <code>POST</code>
and not any other HTTP request, and that both the entry ID and
the URL of the sender are specified.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 TrackbackInfo trackbackInfo<span class="symbol">;</span>
 <span class="keyword">string</span> trackbackTitle<span class="symbol">;</span>
 Uri targetUri<span class="symbol">;</span>

 trackbackInfo <span class="symbol">=</span> <span class="keyword">new</span> TrackbackInfo<span class="symbol">(</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 Id <span class="symbol">=</span> id<span class="symbol">,</span>
 Title <span class="symbol">=</span> title<span class="symbol">,</span>
 BlogName <span class="symbol">=</span> blogName<span class="symbol">,</span>
 Excerpt <span class="symbol">=</span> excerpt<span class="symbol">,</span>
 Uri <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span>url<span class="symbol">)</span>
 <span class="symbol">}</span><span class="symbol">;</span>

 targetUri <span class="symbol">=</span> getTrackbackUrlDelegate<span class="symbol">.</span>Invoke<span class="symbol">(</span>trackbackInfo<span class="symbol">)</span><span class="symbol">;</span>

</pre>
</figure>
<p>If everything is fine, we then construct our <code>TrackbackInfo</code>
object for passing to our delegates, and then try and get the
permalink for the trackback ID.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">if</span> <span class="symbol">(</span>targetUri <span class="symbol">==</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="string">&quot;The entry ID could not be matched.&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span> <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>TrackbackHandler<span class="symbol">.</span>CheckSourceLinkExists<span class="symbol">(</span>targetUri<span class="symbol">,</span> trackbackInfo<span class="symbol">.</span>Uri<span class="symbol">,</span> <span class="keyword">out</span> trackbackTitle<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Format<span class="symbol">(</span><span class="string">&quot;Sorry couldn&#39;t find a link for \&quot;{0}\&quot; in \&quot;{1}\&quot;&quot;</span><span class="symbol">,</span> targetUri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> trackbackInfo<span class="symbol">.</span>Uri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>If we don't have a URL, we return an error code to the sender.</p>
<p>If we do have a URL another method, <code>CheckSourceLinkExists</code> is
called. This method will download the HTML of the caller and
attempt to verify if the senders page does in fact contain a
link matching the permalink. If it doesn't, then we'll abort
here.</p>
<p>If the method is successful and a link is detected, the method
will return the title of the senders HTML page as an out
parameter. This will be used if the trackback information didn't
include a blog name (as this is an optional field).</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>blogName<span class="symbol">)</span><span class="symbol">)</span>
 trackbackInfo<span class="symbol">.</span>BlogName <span class="symbol">=</span> trackbackTitle<span class="symbol">;</span>

 saveTrackbackDelegate<span class="symbol">.</span>Invoke<span class="symbol">(</span>trackbackInfo<span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Success<span class="symbol">,</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">//handle the error.</span>
 result <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode<span class="symbol">.</span>Error<span class="symbol">,</span> ex<span class="symbol">.</span>Message<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Finally, if everything went to plan, we save the trackback to
our data store, and return a success code. In the event of any
part of this process failing, then we return an error result.</p>
<h2 id="downloading-the-senders-html-and-checking-if-a-link-exists">Downloading the senders html and checking if a link exists</h2>
<p>In this implementation, we won't link to the senders site unless
they have already linked to us. We do this by downloading the
HTML of the senders site and checking to see if our link is
present.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">bool</span> CheckSourceLinkExists<span class="symbol">(</span>Uri lookingFor<span class="symbol">,</span> Uri lookingIn<span class="symbol">,</span> <span class="keyword">out</span> <span class="keyword">string</span> pageTitle<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 pageTitle <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> html<span class="symbol">;</span>

 html <span class="symbol">=</span> GetPageHtml<span class="symbol">(</span>lookingIn<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>html<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span> <span class="symbol">|</span> html<span class="symbol">.</span>IndexOf<span class="symbol">(</span>lookingFor<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">,</span> StringComparison<span class="symbol">.</span>InvariantCultureIgnoreCase<span class="symbol">)</span> <span class="symbol">&lt;</span> <span class="number">0</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 HtmlDocument document<span class="symbol">;</span>

 document <span class="symbol">=</span> <span class="keyword">new</span> HtmlDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 document<span class="symbol">.</span>LoadHtml<span class="symbol">(</span>html<span class="symbol">)</span><span class="symbol">;</span>
 pageTitle <span class="symbol">=</span> document<span class="symbol">.</span>GetDocumentTitle<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetPageHtml<span class="symbol">(</span>Uri uri<span class="symbol">)</span>
<span class="symbol">{</span>
 WebRequest request<span class="symbol">;</span>
 HttpWebResponse response<span class="symbol">;</span>
 <span class="keyword">string</span> encodingName<span class="symbol">;</span>
 Encoding encoding<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>

 request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>Create<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>
 response <span class="symbol">=</span> <span class="symbol">(</span>HttpWebResponse<span class="symbol">)</span>request<span class="symbol">.</span>GetResponse<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 encodingName <span class="symbol">=</span> response<span class="symbol">.</span>ContentEncoding<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>encodingName<span class="symbol">)</span><span class="symbol">)</span>
 encodingName <span class="symbol">=</span> <span class="string">&quot;utf-8&quot;</span><span class="symbol">;</span>
 encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>GetEncoding<span class="symbol">(</span>encodingName<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>Stream stream <span class="symbol">=</span> response<span class="symbol">.</span>GetResponseStream<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">using</span> <span class="symbol">(</span>StreamReader reader <span class="symbol">=</span> <span class="keyword">new</span> StreamReader<span class="symbol">(</span>stream<span class="symbol">,</span> encoding<span class="symbol">)</span><span class="symbol">)</span>
 result <span class="symbol">=</span> reader<span class="symbol">.</span>ReadToEnd<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetDocumentTitle<span class="symbol">(</span><span class="keyword">this</span> HtmlDocument document<span class="symbol">)</span>
<span class="symbol">{</span>
 HtmlNode titleNode<span class="symbol">;</span>
 <span class="keyword">string</span> title<span class="symbol">;</span>

 titleNode <span class="symbol">=</span> document<span class="symbol">.</span>DocumentNode<span class="symbol">.</span>SelectSingleNode<span class="symbol">(</span><span class="string">&quot;//head/title&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>titleNode <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 title <span class="symbol">=</span> titleNode<span class="symbol">.</span>InnerText<span class="symbol">;</span>
 <span class="keyword">else</span>
 title <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>

 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;\n&quot;</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;\r&quot;</span><span class="symbol">,</span> <span class="string">&quot;&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">while</span> <span class="symbol">(</span>title<span class="symbol">.</span>Contains<span class="symbol">(</span><span class="string">&quot; &quot;</span><span class="symbol">)</span><span class="symbol">)</span>
 title <span class="symbol">=</span> title<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot; &quot;</span><span class="symbol">,</span> <span class="string">&quot; &quot;</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> title<span class="symbol">.</span>Trim<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>The function <code>GetDocumentTitle</code> uses the <a href="http://htmlagilitypack.codeplex.com/" rel="external nofollow noopener">Html Agility Pack</a>
to parse the HTML looking for the title tag. As the
<code>CheckSourceLinkExists</code> function is only checking to see if the
link exists somewhere inside the HTML you may wish to update
this to ensure that the link is actually within an anchor tag -
the Html Agility Pack makes this extremely easy.</p>
<h2 id="returning-a-response">Returning a response</h2>
<p>In several places, the <code>GetTrackback</code> method calls
<code>GetTrackbackResponse</code>. This helper function returns a block of
XML which describes the result of the operation.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">string</span> GetTrackbackResponse<span class="symbol">(</span>TrackbackErrorCode errorCode<span class="symbol">,</span> <span class="keyword">string</span> errorText<span class="symbol">)</span>
<span class="symbol">{</span>
 StringBuilder builder<span class="symbol">;</span>

 builder <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">using</span> <span class="symbol">(</span>StringWriter writer <span class="symbol">=</span> <span class="keyword">new</span> StringWriter<span class="symbol">(</span>builder<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 XmlWriterSettings settings<span class="symbol">;</span>
 XmlWriter xmlWriter<span class="symbol">;</span>

 settings <span class="symbol">=</span> <span class="keyword">new</span> XmlWriterSettings<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 settings<span class="symbol">.</span>Indent <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 settings<span class="symbol">.</span>Encoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>

 xmlWriter <span class="symbol">=</span> XmlWriter<span class="symbol">.</span>Create<span class="symbol">(</span>writer<span class="symbol">,</span> settings<span class="symbol">)</span><span class="symbol">;</span>

 xmlWriter<span class="symbol">.</span>WriteStartDocument<span class="symbol">(</span><span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteStartElement<span class="symbol">(</span><span class="string">&quot;response&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteElementString<span class="symbol">(</span><span class="string">&quot;response&quot;</span><span class="symbol">,</span> <span class="symbol">(</span><span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>errorCode<span class="symbol">)</span><span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>errorText<span class="symbol">)</span><span class="symbol">)</span>
 xmlWriter<span class="symbol">.</span>WriteElementString<span class="symbol">(</span><span class="string">&quot;message&quot;</span><span class="symbol">,</span> errorText<span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteEndElement<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>WriteEndDocument<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 xmlWriter<span class="symbol">.</span>Close<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> builder<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="implementing-an-mvc-action-for-handling-trackbacks">Implementing an MVC Action for handling trackbacks</h2>
<p>In order to use the handler from MVC, define a new action which
returns a <code>ContentResult</code>. It should only be callable from a
<code>POST</code>, and ideally it shouldn't validate input. Even if you
don't want HTML present in your trackbacks, you should strip any
HTML yourself - if you have ASP.NET validation enabled and an
attempt is made to post data containing HTML, then ASP.NET will
return the yellow screen of death HTML to the sender, not the
nice block of XML it was expecting.</p>
<p>Simply return a new <code>ContentResult</code> containing the result of the
<code>GetTrackback</code> method and a mime type of <code>text/xml</code>, as
shown below.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>AcceptVerbs<span class="symbol">(</span>HttpVerbs<span class="symbol">.</span>Post<span class="symbol">)</span><span class="symbol">]</span>
<span class="symbol">[</span>ValidateInput<span class="symbol">(</span><span class="keyword">false</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">public</span> ContentResult Trackback<span class="symbol">(</span>FormCollection form<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">string</span> xml<span class="symbol">;</span>

 <span class="comment">// get the ID of the article to link to from the URL query string</span>
 <span class="keyword">if</span> <span class="symbol">(</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>form<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">)</span>
 form<span class="symbol">.</span>Add<span class="symbol">(</span><span class="string">&quot;id&quot;</span><span class="symbol">,</span> Request<span class="symbol">.</span>QueryString<span class="symbol">[</span><span class="string">&quot;id&quot;</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="comment">// get the response from the trackback handler</span>
 xml <span class="symbol">=</span> TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>form<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SaveTrackbackComment<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetArticleUrl<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">return</span> <span class="keyword">this</span><span class="symbol">.</span>Content<span class="symbol">(</span>xml<span class="symbol">,</span> <span class="string">&quot;text/xml&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>In this case, I'm also checking the query string for the ID of
the article to link to as we use a single trackback action to
handle all resources. If your trackback submission URL is unique
for resource supporting trackbacks, then you wouldn't need to do
this.</p>
<p>The implementations of your two delegates will vary depending on
how your own website is structured and how it stores data. As an
example I have included the ones used here at Cyotek.com (Entity
Framework on SQL Server 2005 using a repository pattern):</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> Uri GetArticleUrl<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span>
<span class="symbol">{</span>
 Article article<span class="symbol">;</span>
 <span class="keyword">int</span> articleId<span class="symbol">;</span>
 Uri result<span class="symbol">;</span>

 Int<span class="number">32</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>trackback<span class="symbol">.</span>Id<span class="symbol">,</span> <span class="keyword">out</span> articleId<span class="symbol">)</span><span class="symbol">;</span>

 article <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ArticleService<span class="symbol">.</span>GetItem<span class="symbol">(</span>articleId<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>article <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 result <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span>Url<span class="symbol">.</span>Action<span class="symbol">(</span><span class="string">&quot;display&quot;</span><span class="symbol">,</span> <span class="string">&quot;article&quot;</span><span class="symbol">,</span> <span class="keyword">new</span> <span class="symbol">{</span> id <span class="symbol">=</span> article<span class="symbol">.</span>Name <span class="symbol">}</span><span class="symbol">,</span> <span class="string">&quot;http&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">else</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">private</span> <span class="keyword">void</span> SaveTrackbackComment<span class="symbol">(</span>TrackbackInfo trackback<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">try</span>
 <span class="symbol">{</span>
 Comment comment<span class="symbol">;</span>
 Article article<span class="symbol">;</span>
 StringBuilder body<span class="symbol">;</span>
 <span class="keyword">string</span> blogName<span class="symbol">;</span>

 article <span class="symbol">=</span> <span class="keyword">this</span><span class="symbol">.</span>ArticleService<span class="symbol">.</span>GetItem<span class="symbol">(</span>Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>trackback<span class="symbol">.</span>Id<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

 blogName <span class="symbol">=</span> <span class="symbol">!</span><span class="keyword">string</span><span class="symbol">.</span>IsNullOrEmpty<span class="symbol">(</span>trackback<span class="symbol">.</span>BlogName<span class="symbol">)</span> <span class="symbol">?</span> trackback<span class="symbol">.</span>BlogName <span class="symbol">:</span> trackback<span class="symbol">.</span>Uri<span class="symbol">.</span>AbsolutePath<span class="symbol">;</span>

 body <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>AppendFormat<span class="symbol">(</span><span class="string">&quot;[b]{0}[/b]\n&quot;</span><span class="symbol">,</span> trackback<span class="symbol">.</span>Title<span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>Append<span class="symbol">(</span>trackback<span class="symbol">.</span>Excerpt<span class="symbol">)</span><span class="symbol">;</span>
 body<span class="symbol">.</span>AppendFormat<span class="symbol">(</span><span class="string">&quot; - Trackback from {0}&quot;</span><span class="symbol">,</span> blogName<span class="symbol">)</span><span class="symbol">;</span>

 comment <span class="symbol">=</span> <span class="keyword">new</span> Comment<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>Article <span class="symbol">=</span> article<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorName <span class="symbol">=</span> blogName<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorUrl <span class="symbol">=</span> trackback<span class="symbol">.</span>Uri<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>DateCreated <span class="symbol">=</span> DateTime<span class="symbol">.</span>Now<span class="symbol">;</span>
 comment<span class="symbol">.</span>Body <span class="symbol">=</span> body<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>IsPublished <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorEmail <span class="symbol">=</span> <span class="keyword">string</span><span class="symbol">.</span>Empty<span class="symbol">;</span>
 comment<span class="symbol">.</span>AuthorUserName <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">this</span><span class="symbol">.</span>CommentService<span class="symbol">.</span>CreateItem<span class="symbol">(</span>comment<span class="symbol">)</span><span class="symbol">;</span>

 ModelHelpers<span class="symbol">.</span>SendCommentEmail<span class="symbol">(</span><span class="keyword">this</span><span class="symbol">,</span> article<span class="symbol">,</span> comment<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>Url<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>System<span class="symbol">.</span>Exception ex<span class="symbol">)</span>
 <span class="symbol">{</span>
 CyotekApplication<span class="symbol">.</span>LogException<span class="symbol">(</span>ex<span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">throw</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="implementing-an-asp.net-webforms-trackback-handler">Implementing an ASP.NET Webforms trackback handler</h2>
<p>Using this library from ASP.NET webforms is almost as
straightforward. You could, as in the example below, create a
normal page containing no HTML such as trackback.aspx which will
omit the XML when called.</p>
<p>Ideally however, you would probably want to implement this as a
HTTP Handler, although this is beyond the scope of this article.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">using</span> System<span class="symbol">;</span>
<span class="keyword">using</span> System<span class="symbol">.</span>Text<span class="symbol">;</span>
<span class="keyword">using</span> Cyotek<span class="symbol">.</span>Web<span class="symbol">.</span>Trackback<span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> TrackbackHandlerPage <span class="symbol">:</span> System<span class="symbol">.</span>Web<span class="symbol">.</span>UI<span class="symbol">.</span>Page
<span class="symbol">{</span>
 <span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnInit<span class="symbol">(</span>EventArgs e<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">base</span><span class="symbol">.</span>OnInit<span class="symbol">(</span>e<span class="symbol">)</span><span class="symbol">;</span>

 Response<span class="symbol">.</span>ContentEncoding <span class="symbol">=</span> Encoding<span class="symbol">.</span>UTF<span class="number">8</span><span class="symbol">;</span>
 Response<span class="symbol">.</span>ContentType <span class="symbol">=</span> <span class="string">&quot;text/xml&quot;</span><span class="symbol">;</span>

 Response<span class="symbol">.</span>Clear<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 Response<span class="symbol">.</span>Write<span class="symbol">(</span>TrackbackHandler<span class="symbol">.</span>GetTrackback<span class="symbol">(</span>Request<span class="symbol">.</span>Form<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>SaveTrackback<span class="symbol">,</span> <span class="keyword">this</span><span class="symbol">.</span>GetTrackbackUrl<span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> Uri GetTrackbackUrl<span class="symbol">(</span>TrackbackInfo trackbackInfo<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 <span class="keyword">private</span> <span class="keyword">void</span> SaveTrackback<span class="symbol">(</span>TrackbackInfo trackbackInfo<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="providing-the-trackback-url">Providing the trackback URL</h2>
<p>Of course, having a trackback handler is of no use if third
party sites can't find it! For sites to discover your trackback
URLs, you need to embed a block of HTML inside your blog
articles containing a link to your trackback handler. This URL
should be unique for each article. For cyotek.com, we append the
ID of the article as part of the query string of the URL, then
extract this in the controller action, but this isn't the only
way to do it - choose whatever suits the needs of your site.</p>
<p>The following shows the auto discovery information for this URL:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">RDF</span> <span class="name">xmlns:rdf</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/1999/02/22-rdf-syntax-ns#</span><span class="symbol">&quot;</span> <span class="name">xmlns:dc</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://purl.org/dc/elements/1.1/</span><span class="symbol">&quot;</span> <span class="name">xmlns:trackback</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://madskills.com/public/xml/rss/module/trackback/</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">Description</span> <span class="name">rdf:about</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/article/display/creating-a-trackback-handler-using-csharp</span><span class="symbol">&quot;</span> <span class="name">dc:identifier</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/article/display/creating-a-trackback-handler-using-csharp</span><span class="symbol">&quot;</span> <span class="name">dc:title</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">Creating a trackback handler using C#</span><span class="symbol">&quot;</span> <span class="name">trackback:ping</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com/trackback?id=21</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;/</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">Description</span><span class="symbol">&gt;</span>
<span class="symbol">&lt;/</span><span class="name">rdf</span><span class="symbol">:</span><span class="name">RDF</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>It includes the trackback URL (with article ID 21) and the title
of the article, plus the permalink.</p>
<h2 id="next-steps">Next steps</h2>
<p>Cyotek.com doesn't get a huge amount of traffic, and so this
library has not been extensively tested. It has worked so far,
but I can't guarantee it to be bug free!</p>
<p>Possible enhancements would be to add some form of deny list,
so if you were getting spam requests, you could more easily
disable these. Also the link checking could be made more robust
by ensure its within a valid anchor, although there's only so
much you can do.</p>
<p>I hope you find this library useful, the download link is below.
As mentioned, this library uses the Html Agility Pack for
parsing HTML, however you can replace this if required with your
own custom solution.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-09-22 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-a-trackback-handler-using-csharp .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comUsing XSLT to display an ASP.net sitemap without using tablesurn:uuid:6e622ab1-77f6-4c89-ba56-42cf611c08302010-09-21T19:52:22Z2010-02-06T13:13:14Z<p>The quick and easy way of displaying an ASP.net site map
(<code>web.sitemap</code>) in an ASP.net page is to use a <code>TreeView</code>
control bound to a <code>SiteMapDataSource</code> component as shown in the
following example:</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">SiteMapDataSource</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapDataSource</span><span class="symbol">&quot;</span> <span class="name">EnableViewState</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">ShowStartingNode</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">TreeView</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapTreeView</span><span class="symbol">&quot;</span> <span class="name">DataSourceID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">siteMapDataSource</span><span class="symbol">&quot;</span> <span class="name">EnableClientScript</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">EnableViewState</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span> <span class="name">ShowExpandCollapse</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">False</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span><span class="symbol">&lt;/</span><span class="name">asp</span><span class="symbol">:</span><span class="name">TreeView</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p>Which results in a mass of nested tables, in-line styles, and
generally messy mark-up.</p>
<p>With just a little more effort however, you can display the
sitemap using a XSLT transform, resulting in slim, clean and
configurable mark-up - and not a table to be seen.</p>
<p>This approach can be used with both Web Forms and MVC.</p>
<blockquote>
<p>This article assumes you already have a pre-made ASP.net
sitemap file.</p>
</blockquote>
<h2 id="defining-the-xslt">Defining the XSLT</h2>
<p>Add a new <strong>XSLT File</strong> to your project. In this case, it's
named <code>sitemap.xslt</code>.</p>
<p>Next, paste in the mark-up below.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;?</span><span class="name">xml</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span><span class="symbol">?&gt;</span>
<span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">stylesheet</span> <span class="name">version</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">1.0</span><span class="symbol">&quot;</span> <span class="name">xmlns:xsl</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://www.w3.org/1999/XSL/Transform</span><span class="symbol">&quot;</span> <span class="name">xmlns:map</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://schemas.microsoft.com/AspNet/SiteMap-File-1.0</span><span class="symbol">&quot;</span> <span class="name">exclude-result-prefixes</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">output</span> <span class="name">method</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">xml</span><span class="symbol">&quot;</span> <span class="name">encoding</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">utf-8</span><span class="symbol">&quot;</span> <span class="name">indent</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">yes</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mapNode</span><span class="symbol">&quot;</span> <span class="name">match</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMap</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">ul</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">apply-templates</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">ul</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span> <span class="name">match</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMapNode</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">li</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">a</span> <span class="name">href</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">http://cyotek.com{substring(@url, 2)}</span><span class="symbol">&quot;</span> <span class="name">title</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">{@description}</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">value-of</span> <span class="name">select</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">@title</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">a</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">if</span> <span class="name">test</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">map:siteMapNode</span><span class="symbol">&quot;</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">call-template</span> <span class="name">name</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">mapNode</span><span class="symbol">&quot;</span><span class="symbol">/&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">if</span><span class="symbol">&gt;</span>

 <span class="symbol">&lt;/</span><span class="name">li</span><span class="symbol">&gt;</span>
 <span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">template</span><span class="symbol">&gt;</span>
 
<span class="symbol">&lt;/</span><span class="name">xsl</span><span class="symbol">:</span><span class="name">stylesheet</span><span class="symbol">&gt;</span>
</pre>
</figure>
<p><strong>Note:</strong> As generally all URLs in ASP.net site maps start with
<code>~/</code>, the <code>href</code> tag in the above example has been customized to
include the domain <em><a href="http://cyotek.com">http://cyotek.com</a></em> at the start, then use
the XSLT <code>substring</code> function to strip the <code>~/</code> from the start
of the URL. Don't forget to modify the URL to point to your own
domain!</p>
<h2 id="declaratively-transforming-the-document">Declaratively transforming the document</h2>
<p>If you are using Web forms controls, then this may be the more
convenient approach for you.</p>
<p>Just add the <strong>XML</strong> component to your page, and set the
<code>DocumentSource</code> property to the name of the sitemap, and the
<code>TransformSource</code> property to the name of your XSLT file.</p>
<figure class="lang-xml highlight"><figcaption><span>xml</span></figcaption><pre class="code">
<span class="symbol">&lt;</span><span class="name">asp</span><span class="symbol">:</span><span class="name">Xml</span> <span class="name">runat</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">server</span><span class="symbol">&quot;</span> <span class="name">ID</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">xmlSiteMapViewer</span><span class="symbol">&quot;</span> <span class="name">DocumentSource</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">~/web.sitemap</span><span class="symbol">&quot;</span> <span class="name">TransformSource</span><span class="symbol">=</span><span class="symbol">&quot;</span><span class="attribute">~/sitemap.xslt</span><span class="symbol">&quot;</span> <span class="symbol">/&gt;</span>
</pre>
</figure>
<h3 id="programmatically-transforming-the-document">Programmatically transforming the document</h3>
<p>The ASP.net XML control doesn't need to be inside a server side
<code>form</code> tag, so you can use the exact same code above in your
MVC views.</p>
<p>However, if you want to do this programmatically, the following
code works too.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">var</span> xmlFileName <span class="symbol">=</span> Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/web.sitemap&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> xslFileName <span class="symbol">=</span> Server<span class="symbol">.</span>MapPath<span class="symbol">(</span><span class="string">&quot;~/sitemap.xslt&quot;</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> result <span class="symbol">=</span> <span class="keyword">new</span> System<span class="symbol">.</span>IO<span class="symbol">.</span>StringWriter<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="keyword">var</span> transform <span class="symbol">=</span> <span class="keyword">new</span> System<span class="symbol">.</span>Xml<span class="symbol">.</span>Xsl<span class="symbol">.</span>XslCompiledTransform<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

transform<span class="symbol">.</span>Load<span class="symbol">(</span>xslFileName<span class="symbol">)</span><span class="symbol">;</span>
transform<span class="symbol">.</span>Transform<span class="symbol">(</span>xmlFileName<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> result<span class="symbol">)</span><span class="symbol">;</span>

Response<span class="symbol">.</span>Write<span class="symbol">(</span>result<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<h2 id="the-result">The result</h2>
<p>The output of the transform will be simple series of nested
unordered lists, clean and ready to be styled with CSS. And for
little more effort than it took to do the original tree view
solution.</p>
<p>With a bit more tweaking you can probably expand this to show
only a single branch, useful for navigation within a section of
a website, or creating breadcrumb trails.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2010-02-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/using-xslt-to-display-an-asp-net-sitemap-without-using-tables .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com