ASP.NET API CORS

Każda przeglądarka ma wbudowaną ochronę przed wysyłaniem żądań asynchronicznych XMLHttpRequest do serwisów w innej lokalizacji niż w domenie w której aplikacja się znajduje. Nazywane jest to "same-origin policy". Komunikacja aplikacji webowej z serwisem API, które hostowane są w oddzielnych lokalizacjach możliwa dopiero wtedy kiedy zezwolimy w konfiguracji API na połączenia pochodzące spoza domeny w której sam się znajduje. Co przeglądarki rozumieją poprzez "same-origin" najlepiej ilustruje przykład

Adresy "same-origin"

http://blog.kuklis.net/post1
http://blog.kuklis.net/post2

Adresy które nie spełniają polityki "same-origin"

http://www.kuklis.net/ - inny schemat adresu.
http://blog.kuklis.net:9000/post1 - Inny port (UWAGA! dla Internet Explorer jest to nadal "same-origin")
http://www.lukasz.com - inny adres

Aby w aplikacji .NET WebAPI umożliwić podłączenie się klienta z innej domeny niż samo api możemy wykorzystać pakiet Microsoft.AspNet.Cors. W Package Manager Console wpisujemy:

Install-Package Microsoft.AspNet.Cors

Niech w naszym przykładzie aplikacja API będzie postawiona na serwerze po adresem http://localhost:12345 natomiast aplikacja webowa http://localhost:666. W takim przypadku API musi zezwolić na odpowiedź klientowi z adresu localhost:666. W tym przypadku jeżeli wykorzystamy do testów przeglądarkę Internet Explorer zezwolenie nie będzie potrzebne. Dla pozostałych przeglądarek CORS musi być włączony i skonfigurowany

Po dodaniu pakietu należy w pliku WebApiConfig.cs w metodzie Register dodać wywołanie funkcji EnableCors config.EnableCors();

Jeżeli chcemy globalnie zezwolić na połączenia wtedy wpis w ``WebApiConfig.cs``` powinien zawierać konfiguracje CORS.

config.EnableCors(new EnableCorsAttribute("http://localhost:666", "*", "*"));

Jeżeli chcemy zezwolić na zewnętrzne połączenia dla konkretnego controllera bądź konretnej akcji wtedy taka klasę lub metode musimy opatrzyc atrybutem EnableCors pozostawiając w WebApiConfig.cs wywołanie funkcji EnableCors() bez parametru.

[EnableCors("http://localhost:666", "*", "*")]

Ważne jest aby w parametrze `Origins` nie dodać na końcu adresu slasha.

Czym są parametry metody EnableCors ?

  1. Origins - czyli adres(y - oddzielane przecinkiem), które dostaną odpowiedź od naszego serwera API.
  2. Headers - Atrybuty http, które klient może wysłać. Wprowadzjąc customowe headery trzeba pamiętać aby dodać takie atrybuty jak: accept, content-type i origin
  3. Methods - jakiego typu żądania zostaną obsłużone (GET, POST, PUT...)

Więcej informacji pod adresem: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api który był także głównym źródłem wiedzy do napisania powyższego posta.

Pełnoekranowe tło na stronie WWW HTML & CSS

Statyczne tło na całą strone WWW można w łatwy sposób uzyskać poprzez wykorzystanie CSSów i użycie właściwości backgroud-size.

html { 
  background: url(images/bg.jpg) no-repeat center center/cover fixed; 
}

See the Pen fsbackground by Lukasz Kuklis (@lukaszkuklis) on CodePen.

W celu wsparcia starszych wersji przeglądarek należy uzupełnić właściwość o brakujące wpisy:


html { 
  background: url(images/bg.jpg) no-repeat center center/cover fixed; 
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
}

Przyjżyjmy się poszczególnym wartościom. background - to skrót, który w jednej lini pozwala ustawić kilka wartości (można je ustawiać w dowolnej ilości i kolejności):

  • background-attachment: fixed - nakazuje aby tło pozostało nieruchome
  • background-clip : border-box - domyślna wartość, której w naszym przypadku nie wskazujemy. Tło rysowane jest od brzegu krawędzi.
  • background-color: transparent - domyślna wartość.
  • background-image: url(bg.jpg) - wskazujemy na położenie względem naszego pliku html obrazka z tłem.
  • background-position: center - pozycja tła względem wartości (relatywnej) background-origin.
  • background-repeat: no-repeat - nie chcemy aby zdjęcie się powtarzało przy scrollowaniu.
  • background-size: cover - obrazek zostanie wyrenderowany tak aby zapełnić całość konternera.

(background-size podajemy zawsze po background-repeat oddzielając znakiem /. Ewentualnie jako osobną właściwość)

Sqlite i web application

W końcu udało mi się znaleźć chwilę na utworzenie projektu i połączenie się z baza Sqlite. Do czego mi przyda się ta baza ?

  • szybka w przygotowaniu, nie wymaga yinstalowania serwera
  • w sam raz do szybkich, krótkich projektów
  • dobra alternatywa na czas developmentu zanim podłączymy się do głównej bazy

Instalacja pakietu Sqlite:

Install-Package System.Data.Sqlite

Tworzymy bazę danych. Ja zrobiłem to przy użyciu Sqlite Administrator. Po kilku problemach z tymże oprogramowaniem polecam jednak SqlliteStudio (tym bardziej że tworzone przez Polaka)

Utworzyłem baze o nazwie MyDb i dodałem jedną tabelę o nazwie Player. Sqlite table in database

Wstawiłem także jeden rekord do bazy:

Insert into Player (FirstName, LastName, Position) VALUES ('Lukasz', 'Kuklis', 'Attacker')

Nastepnie update connection stringa z nazwa pliku naszej bazy danych i odpowiednim dla Sqlite providerem.


Aktualizujemy zawartość DbProviderFactories do poniższego stanu:





Sprawdzamy czy providers wygląda jak poniżej:


      
      

Dodałem klasę domenowa odwzorowująca utworzoną tabele w bazie danych

public class Player
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Position { get; set; }
}

oraz klasę kontekstu bazy danych

public class FootballContext : DbContext
{
    public FootballContext() : base("name=FootballContext") { }
    
    public virtual DbSet Player { get; set; }
}

Na koniec pobranie danych z bazy oraz przekazanie obiektu do html

IList player;
using (var db = new FootballContext())
{
    player = db.Player.ToList();
}

return View(player.First());

Home Page   My ASP.NET Application

Klasa dla aktywnego elementu menu

Prosta sprawa - chcemy mieć zawsze zaznaczony element menu dla strony która aktualnie wyświetlamy. Proste rozwiązanie - Helper, który będzie przypisywał naszą klasę "aktywności" do elementu, którego controller/action będzie się pokrywał z aktualnymi danymi z ViewContext.RouteData.

 public static MvcHtmlString MenuActionLink(this HtmlHelper htmlHelper, string name, string action, string controllerName, object routeValues, string activeClassName = null, IDictionary htmlAttributes = null)
        {
            var url = new UrlHelper(htmlHelper.ViewContext.RequestContext);
            var anchor = new TagBuilder("a");
            anchor.MergeAttribute("href", url.Action(action, controllerName, routeValues));

            var routeData = htmlHelper.ViewContext.RouteData;
            var currentAction = routeData.GetRequiredString("action");
            var currentController = routeData.GetRequiredString("controller");

            if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) && string.Equals(currentController, controllerName, StringComparison.OrdinalIgnoreCase))
            {
                anchor.MergeAttribute("class", activeClassName ?? "active");
            }

            if (htmlAttributes != null)
            {
                foreach (var htmlAttribute in htmlAttributes)
                {
                    anchor.MergeAttribute(htmlAttribute.Key, htmlAttribute.Value.ToString());
                }
            }
            
            anchor.InnerHtml = name;

            return MvcHtmlString.Create(anchor.ToString(TagRenderMode.Normal));
        }

Orchard Multitenancy Themes

Stawianie tenantów w Orchardzie i przypisywanie specyficznych tematów dla danego tenanta ma jedną wadę. Nie można włączyć tego procesu automatycznego konfigurowania Orcharda. Jest to proces całkowicie manualny. Można jedynie wejść do opcji zarządzania tenantami i zaznaczyć, które tematy chcemy aby były dostępne dla konkretnego tenanta. Druga opcja to edycja pliku settings.txt. Jeżeli chcemy dołączyć możliwość automatycznego ustawiania tematów dla tenantów potrzebujemy polecenia w konsoli Orcharda. Dlatego nie pozostaje nic innego jak utworzyć swój własny moduł z rozszerzeniem możliwości konsoli Orcharda.

Dodatkową komendę dodajemy tworząc nową klasę dziedziczącą z Orchard.Commands.DefaultOrchardCommandHandler. Sam proces dodawania i konfigurowania nowej komendy Orcharda opisany jest tutaj Po utworzeniu nowej metody i wstrzyknięciu serwisu tenantów, pozostaje nam już tylko wykonać update tenanta/ów z listą id (id - nie nameów) wybranych tematów. Całość modułu dostępna jest na GitHubie pod tym linkiem.

IE IFrame i PDF

Internet Explorer ma zadziwiająca moc, dzieki której rosną nasze umiejętności kombinowania i tworzenia przeróżnych workaroundów. Ostatnio poziom adrenaliny podniósł mi feature przeglądarki Microsoftu w którym wyświetlanie plików pdf przez plugin Adobe jest nadrzędne jeśli chodzi o kolejność wyświetlania warstw.

Problem przedstawia się tak:



     

IE IFrame Bug

Niestety w żaden cywilizowany sposób nie da się "przeskoczyć" pliku pdf. Jedyną możliwością jest oszukanie IE i podstawienie pod element który chcemy by był nadrzędny względem iframe drugiego pustego elementu iframe.



        

Dostając porządany efekt: IE IFrame bug fixed