Coding with Titans

so breaking things happens constantly, but never on purpose

Synology, Mono i własny ASP.NET IHttpHandler

Święta, czas lenistwa i objadania, a może by tak w międzyczasie zrobić coś pożytecznego albo czegoś się nauczyć?

Zgodnie z poprzednimi wpisami, najnowsza wersja Synology DSM dostarcza pakiet Mono, który oprócz możliwości uruchamiania aplikacji napisanych w .NET, pozwala też tworzyć własne strony ASP.NET. Co ciekawe obsługuje też ASP MVC, a że wsparcie bazy danych MySQL jest wbudowane i nic nie trzeba robić, żeby z niej korzystać (no może oprócz zainstalowania pakietu phpMyAdmin), to już jest małe pole do popisu dla kogoś, kto nie chce dodatkowo płacić za zewnętrzny hosting. Lub po prostu szuka rozrywki i chce zobaczyć, czy to faktycznie działa, a swoich wrażliwych danych udostępniać poza własne podwórko.

O zwykłych stronkach ASP.NET oraz MVC piszą wszyscy, zatem zaproponuję coś innego. Chcę, aby każdy (w rozumieniu dowolny, o który zapyta przeglądarka) plik z rozszerzeniem “.echo” w dowolnym folderze zwracał tekst “echo” jako odpowiedź z serwera. Proste?

http://<host>/test-site/site.echo ---> "echo"
http://<host>/test-site/wei475kra/aka2kdfhwawo24id09hg.echo ---> "echo"

Ktoś zaraz powie, że to głupie i bezsensowne, ale dla mnie nie do końca. Bo jak mamy taką usługę działającą, to już niewiele trzeba, aby z URLa zrobić REST, a proste zwracanie tekstu “echo” przerobić na odpowiedź z serwera potwierdzającą (lub nie) wykonanie zadanej akcji. Innymi słowy rozszerzona wersja, zapytania zwracałaby uwagę na sam kształt URLa, nazwy pliku oraz danych przesłanych (przy żądaniach HTTP POST request):

http://<host>/test-site/psy/list.echo ---> lista psów
http://<host>/test-site/psy/<id>/details.echo ---> szczegóły konkretnego psa
http://<host>/test-site/psy/<id>/delete.echo ---> usuwa zwierzaka z bazy

Adnotacja: dostęp do bazy MySQL otrzymamy pobierając Connector/Net v6.6.4 i używając w projekcie MySql.Data.dll dla .NET Frameworka v2.0.


Przejdźmy wreszcie do sedna, jak to wszystko poskładać:

  1. Tworzymy nowy projekt ASP.NET Application w Visual Studio dla .NET Frameworka 2.0
  2. Tworzymy własny IHttpHandler, który będzie niedługo obsługiwał zwracanie tekstu ‘echo’ dla wszystkich plików “.echo”. Przykładowo może on wyglądać tak:
public sealed class EchoService : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var response = context.Response;
        response.ContentType = "text/plain";
        response.ContentEncoding = Encoding.UTF8;
        response.Clear();
        response.BufferOutput = false;
        response.Write("echo");
    }

    public bool IsReusable
    {
        get { return true; }
    }
}
  1. Zmieniamy plik Web.config, a dokładnie dodajemy sekcję httpHandlers w system.web tak, aby przekierować obsługę plików z rozszerzeniem “.echo” do naszego nowego serwisu (w miejsce <namespace> należy wstawić dokładną przestrzeń nazw klasy w wynikowym assembly):
<system.web>
   ...

   <httpHandlers>
     <add path="*.echo" verb="*" type="<namespace>.EchoService"/>
   </httpHandlers>

</system.web>
Wydawać by się mogło, że to już wszystko. I faktycznie tak by było, gdybyśmy używali produktów Microsoftu (Cassini, IIS, IIS Express). Jednak w naszym świecie Mono + Synology musimy jeszcze troszkę się pomęczyć.

A mianowicie, gdy spróbujemy teraz wgrać naszą aplikację do folderu **/volume1/web/test-site**, okaże się, że otrzymujemy błąd serwera. Z pomocą przychodzi oczywiście dokumentacja modułu Mono i połączenie go z serwerem WWW Apache. Szczegóły [tutaj](http://mono-project.com/Mod_mono).
  1. Logujemy się z konsoli na Synology i edytujemy plik: mod_mono.conf, dodając do niego między innymi wymuszenie obsługi żądań HTTP dla plików “.echo” przez moduł Mono oraz definiując prywatną aplikację wewnątrz Mono, która sama sobą będzie zarządzać. Bez tego ostatniego kroku Mono po prostu będzie serwować pliki z dysku z zadanej lokacji i oczekiwać, że pliki “.echo” fizycznie istnieją, co przecież nie jest prawdą. Czyli w naszym przypadku cały czas po prostu zwracałoby błąd.
cd /usr/syno/apache/conf/extra
vi mod_mono.conf
I dodajemy w nim na końcu:
AddType application/x-asp-net .echo
Alias /test-site "/volume1/web/test-site"
MonoApplications "/test-site:/volume1/web/test-site"

<Location /test-site>
     SetHandler mono
</Location>
  1. Na koniec już tylko restart sewera Apache WWW
/usr/syno/etc/rc.d/S97apache-user.sh restart

I gotowe! Wesołych świąt!