Pokazywanie postów oznaczonych etykietą Nancy. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą Nancy. Pokaż wszystkie posty

piątek, 16 stycznia 2015

NancyFx Razor Wiele przycisków na jednej stronie (Multiple submit on one form)

Jeżeli zdecydujemy się porzucić stare "web.form" i w warstwie widoku przejść na syntaks Razor, to wtedy spotykamy się z problemem "wiele akcji w jednej formie". O ile podstawowa implementacja wygląda mniej więcej tak:

                <form name="xyzForm" id="xyzForm" class="xyzForm" action="/" method="post">
<input type="submit" value="Generuj raport" id="btnGenerate" class="btnGenerate" />
                </form>

Ten syntaks oznacza, że każde naciśnięcie każdego przycisku (input type="submit") wewnątrz tego form'a spowoduje wywołanie akcji kontrolera:        
        public MainModule(IDataStore dataStore)
        {
            Post["/"] = parameters =>
            {
                 return null;
            };
        }

A co jeśli chcemy, mieć kilka przycisków w jednej formie, odpowiedzialnych za kilka osobnych akcji? Z NancyFx oraz drobną pomocą Ajax'a nie stanowi to żadnego problemu:


                 <form name="xyzForm" id="xyzForm" class="xyzForm" action="/" method="post">
<input type="submit" value="Generuj raport" id="btnGenerate" class="btnGenerate" />
                    <input id="saveReportSpec" type="button" value="Zapisz ustawienia raportu dla klienta" onclick="saveReportSpecJs(); return false;" />
                </form>

        <script type="text/javascript">
            function saveReportSpecJs() {
                var $formData = $('#xyzForm').serialize();

                $.post('/Ajax/SaveReportSpec/', $formData, function (data) 
               {
              //TODO: jeżeli metoda coś zwraca, to tutaj działamy na 'data.'
                }
               );
            }

W przypadku html-a niewiele się zmieniło (dodaliśmy nowy przycisk, tym razem typu button) oraz ustawiliśmy wywoływanie javascriptu pod zdarzeniem "onClick".
Sam javascript, wywołuje nasz kontroler. Co ważne, jako parametr podajemy zserioalizowany formularz (form), dzięki któremu będziemy mogli później w osobnym kontrolerze posługiwać się danymi z formularza.
Prosty kod Modułu (kontrolera) dla tej akcji.

        public AjaxModule(IDataStore dataStore) : base("Ajax")
        {
            Post["/SaveReportSpec"] = _ =>
            {
                return null;
            }
        }

Jako parametr przekazujemy zserializowaną formę, abyśmy później mogli korzystać w kontrolerze z danych zawartych w formie, np.:

<input id="cbXYZ" name="cbXYZ" type="checkbox" />

Odczytujemy za pomocą:

            var cbXYZ= Request.Form.cbXYZ;




czwartek, 18 września 2014

Generowanie PDF z wykorzystaniem Nancy, Pechkin, wkhtmltopdf

Jednym z zadań, jakie dostałem było wygenerowanie raportu, który równocześnie miał się wyświetlać jako html, jak i pdf. Moją pierwszą myślą, było stworzenie tego raportu jako www, a nast. zamiana tego www w pdf. Dodatkowo, chciałem dać użytkownikowi, aby sam wybrał, czy rezultat raportu chce otrzymać jako html, czy jako pdf. Jako web frameworku wykorzystałem nancyfx.

Jako bibliotekę, która zamienia html na pdf, wykorzystałem darmową bibliotekę, dostarczoną przez google, czyli wkhtmltopdf. Ta biblioteka jest dużo lepsza, od konkurencyjnej itextsharp ponieważ itextsharp na chwilę obecną nie radzi sobie z bardziej skomplikowanymi html'ami (w moim przypadku był to brak chart'ów o których wspomniałem w moim poprzednim wpisie).

Aby korzystać płynnie z wkhtmltopdf w .NET, skorzystałem z darmowej biblioteki Pechkin (link do githuba). Przykład wykorzystania pechkin'a z wkhtmltopdf można znaleźć na githubie ale również np. na blogu Ulises Reyes.
Ulises po za kilkoma przykładami użycia, informuje nas również o kilku ważnych aspektach korzystania z Pechkina:

  • It's trivial, here are some gotchas though:
  • GIF Images are not supported
  • Paths to images must be fully qualified as URLs (http://example.com/a/b/image.jpg) or file URIs (file://host/path)
  • Resources over HTTPS cannot be fetched through URLs. You can get around this by specifying the path as a file URIs.
  • References CSS files are supported, I found it easier to write inline CSS (classes in the same document enclosed in a style tag)
Ok. Mamy bibliotekę (napisaną w C), do zamiany html'a w pdf. Mamy adapter, aby wykorzystać tą bibliotekę w .NET. Brakuje nam jeszcze odpowiedniego kodu, aby wykorzystać to w naszej ukochanej Nancy. Na szczęście, natknąłem się na podobny wpis na blogu Shane Mitchell link. Tam wprawdzie była wykorzystywana inna biblioteka, ale nic nie stało na przeszkodzie, aby wszystko połączyć w jedną całość i utworzyć klasę, generującą odpowiedni widok:


using Nancy;
using Nancy.Responses;
using Nancy.ViewEngines;
using Pechkin;
using Pechkin.Synchronized;
using System.Drawing.Printing;
using System.IO;

namespace Nancy.MVC
{
    public static class ResponseAsPDF
    {
        public static Response RenderToPdfPechkin(NancyModule module, string viewName, dynamic model)
        {
            string content = GetPageContent(module, viewName, model);
            SynchronizedPechkin sc = new SynchronizedPechkin(new GlobalConfig().SetMargins(new Margins(0, 0, 0, 0))
                .SetDocumentTitle("Ololo").SetCopyCount(1).SetImageQuality(100)
                .SetLosslessCompression(true).SetMaxImageDpi(-1).SetOutlineGeneration(true).SetOutputDpi(-1).SetPaperOrientation(true)
                .SetPaperSize(PaperKind.Letter));
         
            byte[] pdfBuf = sc.Convert(new ObjectConfig().SetLoadImages(true)
            .SetPrintBackground(true)
            .SetAllowLocalContent(true)
            .SetScreenMediaType(true)
            .SetRunJavascript(true)
            .SetCreateExternalLinks(true), content);
         
            return new StreamResponse(() =>
            {
                var pdfOutput = new MemoryStream(pdfBuf);
                pdfOutput.Seek(0, SeekOrigin.Begin);
                return pdfOutput;
            }, "application/pdf");
        }
        private static string GetPageContent(NancyModule module, string viewName, dynamic model)
        {
            ViewLocationContext viewLocationContext = new ViewLocationContext()
            {
                Context = module.Context,
                ModuleName = module.GetType().Name,
                ModulePath = module.ModulePath
            };
            var rendered = module.ViewFactory.RenderView(viewName, model, viewLocationContext);
            var content = "";
            using (var ms = new MemoryStream())
            {
                rendered.Contents.Invoke(ms);
                ms.Seek(0, SeekOrigin.Begin);
                using (TextReader reader = new StreamReader(ms))
                {
                    content = reader.ReadToEnd();
                }
            };
            return content;
        }
    }
}






środa, 26 lutego 2014

NancyFx - Polskie znaki

Podczas pracy nad nowym projektem, z wykorzystaniem frameworka Nancy (NancyFx) zauważyłem, że mam problem z wyświetlaniem polskich znaków w widoku (Razor). Rozwiązaniem okazało się dodanie metataga <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> do części <head>

Przykładowa część strony wygląda więc tak:

<head>
    <title>Input</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>

 Źródło, z którego wziąłem rozwiązanie.

czwartek, 2 stycznia 2014

NancyFx

O nancy pierwszy raz usłyszałem 28.11.2013r. podczas 70 spotkania Warszawskiej Grupy .NET (WG-.NET). Prezenterem był nie kto inny, jak sam Maciej 'Procent' Aniserowicz, czyli obecnie najpopularniejszy bloger/programista .NET. Tak się składa, że blog Maćka czytam/przeglądam ze zmienną regularnością od dłuższego czasu, więc nie mogło mnie na tym spotkaniu zabraknąć. A co prezentował Maciek? Nowy, lekki,  otwarty (github z kodem źródłowym), web framework, oparty na zasadzie KISS nazwanej tutaj "super-duper-happy-path".

Autorzy sami o swoim dziele napisali kilka słów pod tym linkiem ;)

A więc co tak naprawdę daje nam nancy?
- jest lekkie i wymaga od użytkownika minimum kodu. Przykład? Cały kod potrzebny do napisania aplikacji webowej:
public class SampleModule : Nancy.NancyModule
{
    public SampleModule()
    {
        Get["/"] = _ => "Hello World!";
    }
}
- jest w pełni konfigurowalna, tzn. praktycznie wszystko można skonfigurować/nadpisać. W ost. zawsze można napisać do autorów maila, lub znaleźć odpowiedni fragment kodu i go samemu poprawić (w końcu to open source ;))
- jest całkiem fajnie przemyślany, więc pisze się w tym stosunkowo szybko i wymaga to od programisty naprawdę mało kodu. Dlaczego mała ilość kodu jest ważna rozpisywało się już mnóstwo osób, np: Jeff Atwood - Najlepszy kod to brak kodu w ogole
- jako widok można używać zarówno html, xhtml, aspx, razor jak i wielu innych widoków
- jako kontener standardowy wykorzystywany jest tinyIoC, ale można wykorzystywać też inne
- kod jest łatwo testowalny, co pokazał Christian Horsdal w swojej książce "Instant Nancy web development"  (przykłady z wykorzystaniem xUnit oraz fakeItEasy)
- aplikacje można hostować zarówno w .NET jak i Mono
- logowanie błędów jest dziecinnie proste i zajmuje znacznie mniej miejsca niż w innych aplikacjach 
- dużo lepsze (i łatwiejsze) routingi niż w ASP MVC 4
- bardzo łatwo można przekształcić aplikację webową w konsolową, a nawet w projekt, w którym będą solucje: "core", "web", "console", "test".

Generalnie, po prelekcji Maćka Aniserowicza oraz przerobieniu książki Christiana Horsdala, jestem wielkim optymistą jeżeli chodzi o NANCY. Aż się nie mogę doczekać momentu, w którym będę miał nieco wolnego czasu, aby przerobić jeszcze jakiś tutoriale związane z tą technologią, oraz... zastosowania tej wiedzy w praktyce, w jakimś komercyjnym projekcie.