Miłosz Orzeł

.net, js, html, arduino, java... no rants or clickbaits.

Wysyłanie komunikatu do WebSphere MQ w UTF-8

Jeśli chcesz wysłać do kolejki WebSphere MQ tekst zakodowany w standardzie UTF-8, pamiętaj o ustawieniu właściwości CharacterSet obiektu MQMessage na 1208. Jeśli tego nie zrobisz, tekst zostanie zakodowany z użyciem UTF-16 (CCSID 1200).

MQQueueManager queueManager = new MQQueueManager(...);
MQQueue queue = queueManager.AccessQueue(...);
MQPutMessageOptions putMessageOptions = new MQPutMessageOptions(...);
MQMessage message = new MQMessage();

message.Format = MQC.MQFMT_STRING;
message.CharacterSet = 1208;
message.WriteString("abcąćę");
queue.Put(message, putMessageOptions);

Stringi w .NET kodowane są przy użyciu UTF-16. Czasem jednak warto do wymiany informacji zastosować kodowanie UFT-8. Dlaczego? Wersja 8 jest oszczędniejsza, ponieważ znaki z tabeli US-ASCII są kodowane za pomocą 1 bajta, a nie za pomocą 2, jak w przypadku UTF-16. Jeśli wiec będziesz przesyłał tekst składający się jedynie z tego zestawu znaków, zużyjesz dwa razy mniej miejsca! W przypadku polskich liter znaki zostaną zakodowane na 2 bajtach (tak jak w UTF-16).

Oto porównanie bajtów dla tekstu "abcąćę":

UTF-8    61 62 63 C4 85 C4 87 C4 99
UTF-16   61 00 62 00 63 00 05 01 07 01 19 01

Badanie różnicy kolorów

W .NET Framework do opisu koloru wykorzystywana jest struktura Color. Jej najważniejsze właściwości to: R, G oraz B. Odpowiadają one za intensywność barw podstawowych (czerwonej, zielonej i niebieskiej).* Właściwości te przyjmują wartości z zakresu od 0 do 255. Trzy bajty użyte do opisu składowych dają możliwość stworzenia ponad 16 milionów barw (2^24 = 16 777 216)!

RGB(0, 0, 0) to kolor czarny, (255, 255, 255) to kolor biały, (255, 0, 0) to czerwony a np. (255, 255, 0) to żółty... Struktura Color posiada również właściowść A (kanał alfa), która jest wykorzystywana w grafice komputerowej do określania stopnia przeźroczystości. Wartość 255 oznacza pełną nieprzeźroczystość.

Do zbadania różnicy między kolorami można posłużyć się odległością euklidesową (dystans między punktami). Wyobraź sobie trójwymiarowy układ współrzędnych, których osie oznaczymy odpowiednio przez R, G oraz B (składową A pomijamy). Gdy umieścisz w takim układzie dwa punkty odpowiadające kolorom i zmierzysz odległość między nimi, otrzymasz informację o stopniu podobieństwa kolorów. Dzięki temu będziesz mógł np. wykryć na obrazie wszystkie miejsca mające odcień zieleni...

Color c1 =  Color.White;
Color c2 = Color.Black;

int odlegloscR = (c1.R - c2.R) * (c1.R - c2.R);
int odlegloscG = (c1.G - c2.G) * (c1.G - c2.G);
int odlegloscB = (c1.B - c2.B) * (c1.B - c2.B);

double roznicaKoloru = Math.Sqrt(odlegloscR + odlegloscG
 + odlegloscB);

W powyższym kodzie celowo nie korzystałem z funkcji Math.Pow. Jej zastosowanie przy takiej operacji jak podnoszenie do potęgi całkowitej to marnotrastwo CPU (gdy trzeba przebadać wiele punktów ma to znaczenie). Jeśli wyliczenie zmiennej roznicaKoloru ma słyżyć jedynie porównywaniu barw można by również zrezygnować z pierwiastkowania...

* Model RGB opisuje zjawisko syntezy addytywnej czyli mieszanie się swiatła emitowanego (np. przez monitor).

Kod skryptu aktualizowany w UpdatePanelu

Załóżmy, że z jakiegoś powodu musisz umieścić w obszarze kontrolki UpdatePanel funkcję JavaScript, której kod będzie się zmieniał przy każdej aktualizacji panelu. Funkcja ta ma być wywoływana w odpowiedzi na zdarzenie onclick jakiegoś elementu, który znajduję się poza UpdatePanelem...

Pierwsza rzecz, która przychodzi na myśl to umieszczenie skryptu w kontrolce Literal znajdującej się w aktualizowanym fragmencie strony:

protected void Button1_Click(object  sender, EventArgs e)
{
   string s = "<script type='text/javascript'> function " +
      "test() { alert('" + DateTime.Now + "') };</script>";

   Literal1.Text = s;
}

Niestety, przy próbie wywołania funkcji test() pojawi się błąd (przeglądarka jej nie znajdzie). Dzieje się tak dlatego, że aktualizacja UpdatePanelu to tak naprawdę podmiana właściwości innerHTML warstwy (div), która reprezentuje ten panel po stronie klienta. Wnętrze tej warstwy będzie zawierać fragment skryptu jednak przeglądarka nie będzie sobie zdawać sprawy z jego obecności.

Oto fragment przechwyconej za pomocą narzędzia Fiddler, odpowiedzi serwera na postback pochodzący z UpdatePanelu:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Mon, 29 Sep 2008 13:28:26 GMT
X-AspNet-Version: 2.0.50727
Transfer-Encoding: chunked
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Connection: Close

10c
238|updatePanel|UpdatePanel1|
<input type="submit" name="Button1" value="upa1" id="Button1" />

<script type='text/javascript'> function test() { alert('2008-09-29 15:28:26') };</script> | 22a 216|hiddenField|__VIEWSTATE|/wEP...

Widać wyraźnie, że treść skryptu została wysłana do klienta...

Co zrobić by wywołanie funkcji test(), która pojawiła się po aktualizacji UpdatePanelu, zadziałało prawidłowo? Zamiast dosłownie wpisywać treść skryptu w aktualizowany obszar skorzystaj z metody ScriptManager.RegisterStartupScript:

protected void Button1_Click(object  sender, EventArgs e)
{
   string s = "<script type='text/javascript'> function " +
      "test() { alert('" + DateTime.Now + "') };</script>";

   ScriptManager.RegisterStartupScript(this, typeof(Page),
      "abc", s, false);
}

Tym razem wywołanie metody test() zakończy się powodzeniem (użytkownik zobaczy alert z datą ustawianą w chwili aktualizacji panelu).

O znaczeniu poszczególnych parametrów metody ScriptManager.RegisterStartupScript możesz poczytać tutaj.

Treść komunikatu o błędzie podczas asynchronicznego postbacka

Gdy kontrolka znajdująca się w UpdatePanelu wykonuje asynchroniczny postback skutkujący błędem po stronie serwera, użytkownik zostaje poinformowany o problemie przez komunikat JavaScript. Treść tego komunikatu jest taka sama jak właściwość Message wyjątku. Alert ukaże się jedynie wtedy, gdy witryna nie korzysta z przekierowania w przypadku błędu lub, gdy właściwość AllowCustomErrorsRedirect kontrolki ScriptManager jest ustawiona na false.

Co jeśli chciałbyś zmienić treść wiadomości wyświetlanej użytkownikowi?
Można to zrobić na dwa sposoby. Pierwszy to ustawienie odpowiedniego tekstu w propercie AsyncPostBackErrorMessage kontrolki ScriptManager. Drugi to obsługa zdarzenia AsyncPostBackError tejże kontrolki.

Druga technika daje większe możliwości, ponieważ pozwala na dostosowanie treści komunikatu do typu wyjątku. Dobrym pomysłem jest umieszczenie ogólnej informacji o błędzie we właściwości AsyncPostBackErrorMessage i modyfikowanie jej w razie potrzeby w zdarzeniu AsyncPostBackError.

Tak wygląda deklaracja ScriptManagera wyświetlającego domyślmy komunikat błędu o treści "Mamy problem!":

<asp:ScriptManager  ID="ScriptManager1" runat="server"
 AsyncPostBackErrorMessage="Mamy problem!"
 OnAsyncPostBackError="ScriptManager1_AsyncPostBackError" />

A oto przykładowy kod modyfikujący komunikat wyświetlany w alercie, jeśli błąd powstał na skutek operacji dzielenia przez zero:

protected void ScriptManager1_AsyncPostBackError(object sender,
    AsyncPostBackErrorEventArgs e)
{
    if (e.Exception is DivideByZeroException)
    {
        ScriptManager1.AsyncPostBackErrorMessage = "Nie można dzielić przez zero!";
    }
}

Dostęp do sesji z Page Methods

ASP.NET AJAX oferuje łatwy w użyciu i efektywny mechanizm wywołań metod na serwerze z poziomu JavaScript. Mowa o Page Methods. Ponieważ są one statyczne* nie możesz w kodzie takiej metody użyć dostępu do właściwości Page.Session. Nie oznacza to jednak, że musisz zrezygnować z wykorzystania sesji. Wystarczy, że zamiast Page.Session skorzystasz z HttpContext.Current.Session.

Przykładowo by zapisać i odczytać wartość ze stanu sesji wystarczy użyć kodu:

[WebMethod]
public static void ZapiszDoSession(string s)
{
    HttpContext.Current.Session["abc"] = s;
}

[WebMethod]
public static string OdczytajZSession()
{
    return (string)HttpContext.Current.Session["abc"];
}

A co gdybyś chciał np. pobrać nazwę zalogowanego użytkownika? To równie proste, skorzystaj z HttpContext.Current.User.Identity.Name.

Dostęp do danych sesji jest możliwy, ponieważ przy wysyłaniu zapytań za pośrednictwem obiektu XmlHttpRequest, cookie z identyfikatorem sesji również jest dołączone. Takie ciastko wygląda np. tak:

Cookie: ASP.NET_SessionId=kxxrwp45bhkyyhbzpijo0zj4

* Page Methods są statyczne - i dobrze! Dzięki temu, unika się tworzenia obiektu Page oraz przesyłania ViewState. Serwer ma mniej pracy (nie zachodzą zdarzenia takie jak Page_Load czy Page_PreRender) a ilość danych przesyłanych w sieci jest znacząco mniejsza.