sobota, 16 listopada 2013

Javascript for intermediate with D.Crockford

Dobrze jest znać język, w którym się programuje. Jego założenia, filozofię, składnię, mocne i słabe strony. Jest to szczególnie ważne, gdy specyfika języka jest inna niż specyfika języka, do którego jesteśmy przyzwyczajeni. Takim osobnym, często niezrozumiałym językiem, jest Javascript, który mimo iż posiada podobną składnię do takich języków jak Java czy C# jednak znacznie się od nich różni. Warto więc poświęcić nieco czasu i trochę się nieco o tym języku dowiedzieć (czy to z książek, czy też filmów). Jednym ze źródeł wiedzy o javascript są wykłady Douglasa Crockforda (swoją drogą bardzo znana postać w środowisku Javascropt).

wtorek, 5 listopada 2013

Send SMS message by Mobitex API (HttpWebRequest)

Jedną z podstawowych funkcjonalności niemal każdej aplikacji jest możliwość wysyłania powiadomień mailowych. Coraz częściej zdarza się jednak, że klient chce mieć również możliwość wysyłania krótkich powiadomień tekstowych (tzw. SMS) na telefon komórkowy. Usługę taką w praktyce realizuje się poprzez zewnętrznych partnerów, którzy, w zamian za stosowną opłatę wystawiają nam API, z którym możemy się połączyć. W przeszłości widziałem rozwiązania kilku producentów i... każdy miał na to "swój" sposób (od wystawienia web service, poprzez web request z parametrami wiadomości przesłanymi poprzez URL, na wysyłaniu specjalnie skonstruowanego maila na podaną skrzynkę email ;)).
W takich chwilach jak ta, gdy o tym wspominam zaczynam żałować, że tak późno zacząłem prowadzić mojego bloga ;)

Ale do rzeczy. W tym konkretnym poście opiszę sposób integrowania się z API dostarczonym przez firmę Mobitex. Firma mobitex, wystawia publicznie dostępną specyfikację swojego API (specyfikacja_mt.pdf). Specyfikacja jest krótka, zwięzła i na temat.

W tym konkretnym przypadku, wysyłanie SMS odbywa się poprzez zwykły WebRequest z parametrami przekazanymi w querystringu, a w zamian dostajemy odpowiedź w strumieniu (Stream) za pomocą standardowego WebResponse.

Całość jest całkiem prosta i może być zrealizowana, za pomocą klasy z jedną metodą (co najwyżej należy skonfigurować parametry wedle naszych potrzeb):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Text;
using System.Web;

namespace sms
{
    public class MobitexApi
    {
        public static List<KeyValuePair<string, string>> SendSms(string number, string text)
        {
            List<KeyValuePair<string, string>> keyValueResponse = new List<KeyValuePair<string, string>>();
            try
            {
                string user = "userName";
                string pass = "passInMD51a2bc4f3f52dac6d872ae12";
                string type = "sms";
                string from = "fromUserName";
                string ext_id = "1234";
                string encodedText = HttpUtility.HtmlEncode(text);

                string requestStringUrl = string.Format("https://api.mobitex.pl/sms.php?user={0}&pass={1}&type={2}&number={3}&text={4}&from={5}&ext_id={6}",
                    user, pass, type, number, encodedText, from, ext_id);

                HttpWebRequest HttpWReq = (HttpWebRequest)WebRequest.Create(requestStringUrl);
                //handle error code: ssl_error_bad_cert_domain
                HttpWReq.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(
                delegate
                {
                    return true;
                });
                HttpWebResponse HttpWResp = (HttpWebResponse)HttpWReq.GetResponse();

                Stream response = HttpWResp.GetResponseStream();
                StreamReader reader = new StreamReader(response);
                string responseString = reader.ReadToEnd();
                HttpWResp.Close();

                var respStringTable = responseString.Split(',');

                for (int i = 0; i < respStringTable.Length; i++)
                {
                    string keyValue = respStringTable[i];
                    if (!string.IsNullOrEmpty(keyValue))
                    {
                        string key = keyValue.Substring(0, keyValue.IndexOf(':'));
                        string value = keyValue.Substring(keyValue.IndexOf(": ") + 1);

                        keyValueResponse.Add(new KeyValuePair<string, string>(key, value));
                    }
                }
            }
            catch (Exception ex)
            {
                //TODO:
                //Error handling in here (for example NLog)
            }
            return keyValueResponse;
        }
    }
}
Całość rozwiązania sprowadza się, do utworzenia odpowiedniego querystringa, w którym przekazujemy wszystkie interesujące nas parametry (patrz dokumentacja), a nast. tworzymy web request. Przed wykonaniem dodajemy obsługę eventu "ServerCertificateValidationCallback". Jest to spowodowane tym, że mobitex posiada nieprawidłowy certyfikat bezpieczeństwa i w trakcie wykonywania httpWebRequest dostajemy pytanie czy jesteśmy tego świadomi i mimo to chcemy kontynuować naszą akcję. Po wszystkim, dostajemy odpowiedź z mobitexu, która wyglada mniej więcej tak:
Status: 002, Id: 03a72a49fb9595f3737bc4a2519ff283, Number: 4860X123456
 więc parsujemy ją do nieco bardziej przyjaznego w obsłudze typu danych, czyli kolekcji obiektów typu "KeyValuePair".

Mobitex posiada też możliwość sprawdzenia stanu konta. Czytając dokumentację techniczną brakuje mi natomiast jakiegoś raportu podsumowywującego wszystkie sms wysłane w danych (sparametryzowanym) okresie czasu z danego konta (gdybym chciał zobaczyć datę, status oraz num. tel na jakie wysłałem wszystkie sms powiedzmy w poprzednim mieś.) i myślę, że w najbliższym czasie im takie pytanie zadam.


p.s. W przypadku korzystania z sieci korporacyjnych, należy się upewnić, aby mieć odblokowany port 443 dla serwera 213.5.10.22.
p.s.2 Uwaga. Nr. tel. w parametrze powinien być w formacie 4860X123456
p.s.3  ServerCertificateValidationCallback dostępny jest dopiero w .NET 4.5

sobota, 2 listopada 2013

Chrome DevTools

Jak powszechnie wiadomo, każda praca wymaga specyficznych dla tej pracy narzędzi. W przypadku hydraulika jest to klucz francuski, w przypadku nauczyciela jest to tablica, a przypadku web developera są to tzn. 'DevTools'. Osobiście, do tej pory, jako 'DevTools' używałem narzędzi dostępnych w Internet Explorer (od wersji 8.0). Owszem, byłem świadomy "firebuga" występującego w 'mozilla firefox', jednak siła przyzwyczajenia była zbyt silna, aby się przemóc.'Narzędzia developerskie IE' pokazał jeden z kolegów z pierwszej pracy i... tak jakoś zostało. Do dzisiaj.

Pod wpływem kilku ost. wykładów Gutka (więcej info w moim poprzednim wpisie), postanowiłem poszerzyć moją wiedzę o to, co zaserwował nam Gutek podczas swoich wykładów.

Jednym z miłych zaskoczeń są narzędzia developerskie dostarczone nam przez firmę Google dla ich przeglądarki Chrome o nazwie "Chrome DevTools".

To co jest naprawdę fajne, to fakt, iż google, oprócz dostarczenia nam porządnych narzędzi udostępnia również mini-tutorial, w którym oprócz "wykładów" (filmiki) mamy też do wykonania część praktyczną (ponieważ jak to mówi miły pan na filmiku "nic nie zastąpi praktyki" ;)).

To co mnie najbardziej urzekło w "Chrome DevTools", to możliwość sprawdzania wydajności stron i sposób konkretnego wyszukiwania "wąskiego gardła" (tzw. "bootle neck"), z dokładnością do linii kodu, zarówno przy ładowaniu strony do przeglądarki, jak i późniejsza obsługa tego kodu w przeglądarce.

Link do samouczka z Chrome DevTools


p.s. W kursie jest również odnośnik do innej strony hostowanej przez google (closure-compiler), służącej do minimalizowania objętości plików js (w celu zwiększenia wydajności stron, które tworzymy).

niedziela, 27 października 2013

JavaScript Intellisense w VS 2012

Ostatnimi czasy, miałem okazję uczestniczyć (jako widz), w dwóch wykładach prowadzonych przez Jakuba Gutowskiego o wdzięcznym tytule "Javascript dla c# developera". Pierwsza prezentacja odbyła się 12 października podczas konferencji "on-line" dotnetconf.pl (można ją obejrzeć na youtube.com). Druga odbyła się 17 października podczas 69 spotkania wg.net (Warszawska Grupa .NET). (materialy ze spotkania)
Generalnie Gutek pokazał kilka ciekawostek związanych z językiem javascript, oraz zachęcał do zapoznania się i stosowania kilku narzędzi. Zanim jednak przejdziemy do esencji tego, o czym mówił i co chciał nas nauczyć, najpierw mała przekąska, tzw. "snack", czyli repost z bloga Gutka, nt. włączenia intelisense dla języka javascript w Visual Studio 2012 link do blogu Gutka jak włączyć intelisense w VS 2012.


p.s. Gdyby kiedyś coś się stało z blogiem Gutka, to należy utworzyć w solucji katalog przeznaczony dla plików javascript i dodać tam plik plik.js, a nast. wejść do Narzędzia->Opcje->Edytor Tekstu->Javascript->IntelliSense->Odwołania-> ustawić grupę odwołań (np. Implict (Web)), a nast. wpisać ścieżkę względną do naszego pliku i go 'Dodać'.  Od tego momentu, mamy działające intellisense dla naszych plików js :)

NLog, czyli logowanie błędów w .NET

Jakiś czas temu, opisywałem na moim blogu bardzo popularny logger dla .NET, o nazwie log4net. Sam log4net jest bardzo ok, i w zasadzie całkiem nieźle się sprawdza w swojej roli, jednak jak to mówią "lepsze jest wrogiem dobrego", a tym lepszym, a przynajmniej wygodniejszym narzędziem jest nlog.

Główne różnice, wady/zalety?
- NLog jest wygodniejszy. Co musimy zrobić? Ddokładamy bibliotekę do projektu,  za pomocą Packer Menager Console pobieramy plik konfiguracyjny, odkomentowywujemy ustawienia i... działa. W przypadku log4net trzeba pamiętać o sekcji inicjalizacji w 'global.asax' oraz odpowiednim ustawianiu pliku konfiguracyjnego
- zaawansowana konfiguracja -> tutaj akurat nie miałem okazji ostro sprawdzać obu narzędzi, jednak czytając różne blogi, zazwyczaj ludzie chwalą sobie intuicyjność oraz bardziej elastyczną konfigurację NLoga
- aktualizacje -> ost. aktualizacja nlog wyszła 09.10.2013, i jest w miarę często aktualizowany (projekt żyje), natomiast odnośnie log4net spotkałem się z opiniami, że ost. aktualizacja była ponad 5 lat temu
- autor nLog'a jkowalski.com na jednym ze spotkań Warszawskiej grupy .NET wykazywał niewielkią, ale jednak wyższą wydajność swojego rozwiązania nad rozwiązaniem log4net (i zdecydowanie wyższą nad standardowym System.Diagnostics).

Osobiście uważam, że nlog jest zwyczajnie wygodniejszy, oraz "nowszy", przez co niewiele, ale jednak lepszy niż log4net.

Całkiem fajnie opisał ten temat Maciej 'procent' Aniserowicz na swoim blogu.
Dobry post z porównaniem jest też na stackoverflow.com - log4net vs nlog
Opis NLog na codeproject.com


p.s. przykładowa konfiguracja, którą stoduje w klasie Bootstrapper projektu Nancy:
        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
        {
            base.ApplicationStartup(container, pipelines);
            #region event logger (nLog)
            FileTarget fileTarget = CreateNlogFileConfiguration();
            SimpleConfigurator.ConfigureForTargetLogging(fileTarget, LogLevel.Info);
            LogAllRequests(pipelines);
            LogAllResponseCodes(pipelines);
            LogUnhandledExceptions(pipelines);
            #endregion
        }
        private static FileTarget CreateNlogFileConfiguration()
        {
            //SimpleConfigurator.ConfigureForTargetLogging(new AsyncTargetWrapper(new EventLogTarget()));
            FileTarget fileTarget = new FileTarget();
            fileTarget.Name = "CeidgCheckLogger";
            fileTarget.CreateDirs = true;
            fileTarget.Layout = "${date} ${level} ${callsite:className=true:includeSourcePath=false:methodName=true} ${message}";
            //fileTarget.FileName = @"${basedir}/logs/CeidgCheckNancy.${date:format=yyyy.MM.dd}.log"; //for catalog in solition
            fileTarget.FileName = @"D:/logs/CeidgCheckNancy/CeidgCheckNancy.${date:format=yyyy.MM.dd}.log";
            fileTarget.KeepFileOpen = false;
            fileTarget.Encoding = Encoding.UTF8;
            return fileTarget;
        }
        private void LogAllRequests(IPipelines pipelines)
        {
            pipelines.BeforeRequest += ctx =>
            {
                log.Info("Handling request {0} \"{1}\"",
                ctx.Request.Method, ctx.Request.Path);
                return null;
            };
        }
        private void LogAllResponseCodes(IPipelines pipelines)
        {
            pipelines.AfterRequest += ctx =>
            log.Info("Responding {0} to {1} \"{2}\"",
            ctx.Response.StatusCode, ctx.Request.Method,
            ctx.Request.Path);
        }
        private void LogUnhandledExceptions(IPipelines pipelines)
        {
            pipelines.OnError.AddItemToStartOfPipeline
            ((ctx, err) =>
            {
            log.ErrorException(string.Format("Request {0}\"{1}\" failed", ctx.Request.Method, ctx.Request.Path), err);
            return null;
            });
        }

czwartek, 24 października 2013

.NET Client consuming Java IBM WebSphere Web Service

W dwóch moich poprzednich postach opisywałem sposób podłączenia się aplikacji .NET do web serwisów napisanych w PHP z pomocą Zend Framework (link1 link 2 ) i związanymi z tym problemami. W tym wpisie skupie się na podłączeniu się do web service utworzonego za pomocą IBM WebSphere wykorzystującym standardy technologii web services napisanych dla środowiska J2EE (tzw. "Java korporacyjna").

Do poprawnego 'skonsumowania' serwisu używamy standardowego mechanizmu WCF poprzez "Add Service Reference". Po wpisaniu adresu serwisu (z końcówką ?wsdl) powinniśmy wykryć serwis, a nast. utworzyć na jego podstawie klasę proxy. Gdyby automat nie wykrył usługi, to należy wpisać adres usługi do przeglądarki, a nast. zapisać plik na dysk z rozszerzeniem .xml (i w Add Service Reference podać ścieżkę na dysku gdzie zapisaliśmy ten plik). W przypadku mojej usługi, konieczne było jeszcze zgranie pliku .xsd (na szczęście znajdował się pod tym samym adresem co serwis, tylko z innym rozszerzeniem).

W momencie, gdy mamy utworzoną klasę proxy, możemy spróbować ją wywołać. Niestety w moim przypadku, standardowe wywołanie klasy proxy oznaczało błąd:
„Atrybut System.Xml.Serialization.XmlAttributeAttribute obiektu XmlSerializer nie jest prawidłowy w parametrXYZ. Kiedy atrybut IsWrapped ma wartość true, obsługiwane są tylko atrybuty XmlElement, XmlArray, XmlArrayItem i XmlAnyElement.”
Poprosiłem więc o pomoc bardziej doświadczone osoby i one zasugerowały mi aby,  w miejscu, w którym chcemy wywołać usługę dodać przestrzeń nazw:
using System.ServiceModel
 a w kodzie aplikacji EndPoint:
EndpointAddress ea = new EndpointAddress("https://abcd/hash/ws/xyz");
WS ws = new WSClient(new BasicHttpBinding(BasicHttpSecurityMode.Transport), ea);
Posiadając tak wygenerowaną klasę proxy, zasugerowano mi wejść w obiekt "WS" ("go to definition") i tam sprawdzić klasy, które wykorzystują 'parametrXYZ'.
Okazało się, że faktycznie, były tam dwie klasy, które wykorzystywały taki parametr. Rozwiązaniem tego problemu okazało się przemodelowanie tych klas, poprzez implementację System.ComponentModel.INotifyPropertyChanged czyli dodanie i obsługę zdarzenia  
 public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
oraz dodanie zmiennych prywatnych każdemu odpowiadającemu mu publicznemu propertisowi, a także usunięcie atrybutu [System.ServiceModel.MessageBodyMemberAttribute] dla tych propetrtisów.
Przykład poprawnie poprawionej klasy:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.MessageContractAttribute(WrapperName="parametr-XYZ", WrapperNamespace="http://xyz.pl/ab/xyz", IsWrapped=true)]
    public partial class xyzRequest : object, System.ComponentModel.INotifyPropertyChanged {

        private string parametrXYZField;
        [System.Xml.Serialization.XmlAttributeAttribute("parametr-XYZ")]
        public string parametrXYZ
        {
            get
            {
                return this.parametrXYZField;
            }
            set
            {
                this.parametrXYZField= value;
                this.RaisePropertyChanged("parametrXYZ");
            }
        }
       
        public xyzRequest()
        {
        }

        public xyzRequest(string parametrXYZ)
        {
            this.parametrXYZField = parametrXYZ;
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null))
            {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }
Czynność taką powtarzamy dla każdej klasy (i propertisu w tej klasie), w której występuje problematyczny dla nas parametr "parametrXYZ". W moim konkretnym przypadku to wystarczyło, a wywołanie usługi nast. poprzez:
string fuu = "Bar";
var raport = ws.pobranieraportu(fuu);

Korzystanie z web serwisów napisanych dla J2EE w .NET może nieść za sobą inne problemy np:
„Element XML 'rezultat-xyz' z obszaru nazw 'http://xyz.pl/ab/xyz' odwołuje się do metody i typu. Zmień nazwę komunikatu metody, używając atrybutu WebMethodAttribute, lub zmień element główny typu, używając atrybutu XmlRootAttribute.”
Powodem błędu jest zastosowanie dla kilku operacji: takiej samej nazwy typu zwracanego, którą jest „rezultat-xyz”. Technologia C#/.NET przewiduje obecność tylko unikalnych nazw elementów zwracanych przez poszczególne operacje. 
W celu wyeliminowania błędu niezbędna jest ręczna modyfikacja automatycznie wygenerowanej klasy Proxy. Modyfikacja polega na nadaniu unikalnych nazw elementów zwracanych przez wszystkie operacje dostępne w WebService, których nazwy elementów zwracanych kolidują ze sobą.

Inne problemy mogą wynikać z niestarannie napisanego XSD, lub innych, trudnych do zidentyfikowania powodów.



środa, 23 października 2013

SQL DateTime i DateTime2

Całkiem niedawno, tworząc insert do bazy danych wygenerowanej przez EF spotkałem się z błędem odnośnie parsowania dat z 'datetime2' na 'datetime'. Z tego co zrozumiałem, chodziło o brak inicjacji zmiennej (bo jak wiadomo, .NET Datetime ma domyślą wartość roku 0, a T-SQL Datetime zaczyna się od 1753 roku). Zainicjowanie tej zmiennej na "DateTime.Now();" rozwiązało ten problem, za to, krótkie spojrzenie na forum stackoverflow.com wyjaśnia nam dodatkowe niuanse pomiędzy tymi typami, taki jak np. precyzja dokładności "3 1/3 milliseconds" do "100ns".

Link do do zgłoszenia na stackoverflow