Pure FP Webflux i autoryzacja

Pure FP Webflux i autoryzacja
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

Hej, piszę swój pierwszy czysto funkcyjny serwer w Webfluxie. Obejrzałem kilka prezek i githubów, ale chcę rozwiać wszelkie wątpliwości.

  1. Jeśli nic nie skonfiguruję to domyślnie Netty będzie obsługiwał żądania na 1 wątku ? Dobrą praktyką jest ustawić tyle wątków ile obsługuje procesor czy są inne podejścia ? Są jakieś wrażliwe miejsca, w których muszę pamiętać, że żądania współdzielą wątek i martwić się o race conditions itd ? Pytam o przykre doświadczenia ludzi, którzy więcej tak pisali. Generalnie czy takie rozwiązanie z nieblokującą architekturą jest słuszne produkcyjnie ?

  2. Autoryzacja robione przez high order function. Z tego co zrozumiałem zamysł jest taki, że user loguje się, z bazki userów jest on wyciągany po loginie i sprawdzane jest czy zahashowane hasło różni się z tym jego i jeśli tak to generowany jest JWT, który również gdzieś zapisujemy do in memory ConcurrentHashMapy jako klucz i wartość czyli dane sesji (id usera, wygaśnięcie). Security high order function wygląda tak, że z z headera jest token wyciągany, sprawdzane jest czy sesja w tej concurrent hash mapie istnieje i ewentualne sprawdzenie czy nie wygasła ? Czy to jest też produkcyjnie ok?

edytowany 1x, ostatnio: Bambo
W0
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 4 godziny
  • Postów:3539
1
Bambo napisał(a):

Hej, piszę swój pierwszy czysto funkcyjny serwer w Webfluxie. Obejrzałem kilka prezek i githubów, ale chcę rozwiać wszelkie wątpliwości.

  1. Jeśli nic nie skonfiguruję to domyślnie Netty będzie obsługiwał żądania na 1 wątku ? Dobrą praktyką jest ustawić tyle wątków ile obsługuje procesor czy są inne podejścia ?

WebFlux sam z siebie jest nakładką na SpringBoota - jak masz pod spodem Tomcata, to zależy on od konfiguracji Tomcata. Jak masz pod spodem Reactora, to zależy on od Reactora. Reactora ma trochę inny model pracy niż tradycyjne serwery aplikacji - zamiast iluś tam wątków tworzy sobie pod spodem tzw. Worker Thready, do których trafiają zadania (coś trochę jak system agencki). Jest ich chyba tyle, co procków z defaultu.

Są jakieś wrażliwe miejsca, w których muszę pamiętać, że żądania współdzielą wątek i martwić się o race conditions itd ?

Race conditions będzie raczej problemem gdy wskoczysz do swojego kodu, tj. jeśli napiszesz architekturę tak, że będziesz miał wspólne zasoby nie będące thread-safe. Tzn. wszelkiego rodzaju mutowalne static'i oraz repozytoria typu baza danych itp. Tych pierwszych unikaj, te drugie należy dodatkowo zabezpieczać.

Pytam o przykre doświadczenia ludzi, którzy więcej tak pisali. Generalnie czy takie rozwiązanie z nieblokującą architekturą jest słuszne produkcyjnie ?

IMO tak, tzn. nigdzie jeszcze nie spotkałem się z większymi problemami z tym. Spotkałem się za to z problemami przy np. Weblogic'u, przy dosyć niskich liczbach użytkowników. Jeśli nie planujesz robić naprawdę dużego serwisu to konfiguracja domyślna powinna ogarnąć temat, jeśli chodzi o większy serwis to widzę pewien problem o którym wspomniałem dalej.

  1. Autoryzacja robione przez high order function. Z tego co zrozumiałem zamysł jest taki, że user loguje się, z bazki userów jest on wyciągany po loginie i sprawdzane jest czy zahashowane hasło różni się z tym jego i jeśli tak to generowany jest JWT, który również gdzieś zapisujemy do in memory ConcurrentHashMapy jako klucz i wartość czyli dane sesji (id usera, wygaśnięcie). Security high order function wygląda tak, że z z headera jest token wyciągany, sprawdzane jest czy sesja w tej concurrent hash mapie istnieje i ewentualne sprawdzenie czy nie wygasła ? Czy to jest też produkcyjnie ok?

Pytanie, co zamierzasz z tym zrobić. Bo np. jeśli ta HashMapa będzie zbyt duża to skończy ci się pamięć, dodatkowo takie rozwiązanie jest raczej słabo skalowalne (zakładasz istnienie jednej instancji - bo przy dwóch musisz jakoś te tokeny synchronizować).

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
3

Ja tak piszę i popularyzuję. Webflux czysto funkcyjny jest dla mnie mniejszym złem niż klasyczny Spring. Ale

ad 1 . To domyślnie nie będzie jeden wątek, ale chyba tyle co masz rdzeni - zapomniałem już co dokładnie. Da się to ustawiać.
(tu dla zabawy ustawione 300 wątków - https://github.com/javaServerFun/betterBadFibonacci/blob/full/src/main/kotlin/pl/setblack/fibo/FiboServer.kt)

Zasadniczo 1 watek na rdzeń ma sens (z punku widzenia low latency i efektywnościoprzetwarzania), ale są wyjątki.
Wyjątki są takie jeśli część twojej architektury jest jakoś blokująca (np jdbc). Wtedy może być tak, że warto mniej - jęsli blokującytm zadaniom wydzelisz inną pulę wątków, albo więcej - jesli czasem blokujące zadanie poleci z puli netty (niby się nie powinno tego robić..ale bywa i działa).
(btw. widziałem całkiem krytyczną aplikację, która naprawdę grube operacje na jdbc robi blokujaco na wątkach netty i (to straszne, ale działa) bez problemu- podwyższyli chłopaki po prostu liczbę wątków i zrobili z netty tomcata :-) ).

Produkcyjnie WebFLux mi działa, ale akurat mam dość specyficzne systemy - nie mam nigdzie WebFluxa ostro online obciążonego, a load testy wiadomo, że świnie. (Mam całkiem niezłą produkcję na gołym netty i javie, ale to inna historia).

Mam inny wniosek - dotyczący generalnie programowania reaktywnego, kóre się z takim WebFluxem uprawia:
** java się do tego zupełnie nie nadaje** na większą skalę trudno utrzymać kod czytelnym (da się, ale strasznie to męczące).
Kotlin nadaje się prawie (to uprawiam).

ad 2. z JWT generalnie nic nie musisz cachować w ConcurrentHashMap, możesz za każdym razem po prostu sprawdzać podpis. ale możesz dla "szybklości" taki cache zrobić (tak się robi).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 8x, ostatnio: jarekr000000
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
1

Dzięki za odpowiedzi.

@jarekr000000 piszę w Kotlinie, a nie w Javie.

Jeszcze czegoś tu nie rozumiem .. jeśli robię WebFluxa jako aplikację Spring Bootową - normalnie z adnotacjami to mimo wszystko wstaje mi Netty. Wtedy mimo wszystko mam architekturę nieblokującą ?

Chcę to napisać bez adnotacji i beanów z tego względu, że generalnie odkąd dowiedziałem się o czymś takim jak Spring (i generalnie o 1 frameworku webowym w moim życiu) oraz JPA zastanawiałem się gdzie tu jest w tym wszystkim ułatwienie skoro tak naprawdę nie wiadomo co się dzieje (rozkminiałem to zanim zacząłem oglądać konfy). Konfiguracja security w beanie to dla mnie ostra patola, generalnie wolę mieć wszystko napisane i widoczne w kodzie a nie zgwałcone gdzieś przez framework.

Z tymi pytaniami o zastosowania tego produkcyjnie chodziło mi o to czy taki serwerek będzie się sprawdzał przy aplikacji społecznościowej, która może mi się rozrastać. Póki co ma planowany ruch na max 50 userów, ale kto wie co dalej :D Chciałbym przewidzieć jakie problemy i trudności może mi sprawić napianie tego w taki czysty sposób bez całych bajerów springowych, bo zakładam, bo miimo wszystko o więcej rzeczy muszę się martwić niż przy klasycznym podejściu - chociażby thread safe,

edytowany 1x, ostatnio: Bambo
KamilAdam
masz +1 za stwierdzenie, że Spring gwałci aplikacje. Trzeba by to na koszulkach drukować
Bambo
No generalnie te wszystkie książki i tutoriale jak się nagle po czystej Javie tyka takiego Springa czy JPA to jest niezłe rozwolnienie mózgu :D W ogóle swojego czasu słyszałem opinie, że im więcej adnotacji i aopów tym bardziej pro kod xD
jarekr000000
Gdyby ludzie te książki czytali to bym tak bardzo Springa, JPA i JSF i ZombiEE nie zwalczał. Ale prawda jest taka, że nie czytają i w tej sytuacji to są po prostu granaty w rękach małp. Z takim WebFluxem i Kotlinem jest o tyle lepiej, że po prostu większości kod nawet się nie skompiluje :-)
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
4

Po to stworzono architektury nieblokujące, aby wytrzymać bardzo duży ruch.
Ale czy faktycznie wytrzyma zależy od tego czy:

  • chłopaki od WebFlux czegoś nie zrypali - raczej nie. To Spring, mam zaufanie do ich jakości zasadniczo - nawet jak coś spsują to naprawiają w mniej niż 2 lata.
  • czy ty w swojej aplikacji czegoś nie zepsujesz - no i tu zależy. Niestety kotlin i funkcyjne podejście Cię nie chronią, aż tak bardzo jak by się chciało.

Chcesz mieć bezpieczeństwo -- napisz testy obciążeniowe i w miarę jak aplikacja będzie używana weryfikuj ich miarodajność ( np. sprawdzaj statystykami co najczęściej robią użytykownicy).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 3x, ostatnio: jarekr000000
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

A takie funkcyjne endpointy łatwo udokumentować jakimś swaggerem ?

Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Postów:1873
2

Swagger wspiera WebFluksa podobno dopiero od wersji 3.0.0, która obecnie jest w snapshocie. Możesz użyć https://stackoverflow.com/questions/55138027/spring-rest-docs-with-junit-5-and-webflux


”Engineering is easy. People are hard.” Bill Coughran
edytowany 1x, ostatnio: Charles_Ray
jarekr000000
A to nie jest tak, że wspierany jest tylko ten kijowy WebFlux na adnotacjach?
Charles_Ray
Tak mi się niestety zdaje, chociaż MVC też można robić deklaratywnie, więc może dodadzą.
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

Dzięki. Tak jeszcze jak to piszę to zastanawiam się jak łączyć Vavra i Mono/Fluxy .. No bo bez sensu, żeby serwisy czy repo zwracały Mono<Option<T>> chyba nie ? :D Jak to ogrywacie ? Either jeszcze rozumiem bo mamy powód, ale mimo wszystko takie Mono<Either<Error, Object>> wygląda dziwnie

Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Postów:1873
0

Ja bym już się trzymał Reactora i programowania reaktywnego.


”Engineering is easy. People are hard.” Bill Coughran
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Co do zwracania Mono<List<Option<Either<Srajder< .itd. tojakimś rozwiązaniem są typealiasy. Masz kotlina - używaj. Dodatkowa zaleta to możliwość szybszego refaktoringu (wiadomo gdzie taki typ specyficzny jest potem używany).

Choć nie ukrywam, że wymyslanie nazw na Mono<Either<E,Pies>> też jest wyzwaniem - PiesSource, PiesChance ?IsPies, ?

Vaughn Vernon (ten od DDD) strasznie się swego czasu wkurzał na to Mono i proponował jeszcze większsego raka IMO (jakieś tam Continuable czy coś).
Ze względu na składanie wyników Mono<CośTam> jest całkiem spoko w serwisach. Ludzie niech sie przyzwzajają do flatMapowania :-)

Jak robisz coś bardziej jak bibliotekę to możesz wystawiać Publishera.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Zobacz pozostałe 20 komentarzy
Charles_Ray
Mono.error(new MyException())
jarekr000000
Jesli to exception nie jest Throwable to się nawet nie skompiluje.
Charles_Ray
@jarekr000000 to jest oczywiste. Chodziło mi o to, że można rzucać własny wyjątek, co w sumie też jest oczywiste.
Michał Sikora
Michał Sikora
@Charles_Ray: Nie, nie chodzi o hot publisher. Chodzi o strumień, który nie ma końca. Np. kliknięcia w przycisk albo lokalizacja użytkownika. I nie wiem jak tu pomagają operatory same w sobie, jeżeli nie jestem w stanie opakować tego w jakąś wartość. A czy nazwę sobie to Either, Albo, Result, UiModel to już nie ma znaczenia.
jarekr000000
No właśnie o to cały ten ambaras z Eitherem itp. nie każdy chce mieć wyjątek. Względy wydajnościowe są drugorzędne przy tym. Głównie chodzi o testowalnośc i type safety kodu (czyli utrzymywalność).
azalut
  • Rejestracja:około 12 lat
  • Ostatnio:ponad rok
  • Postów:1129
2

moglibyście podać jakis przykład takiego projektu w spring 5 który nie używa adnotacji? chodzi wam o zupełne nieużywanie kontenera zalezności w kontekscie wstrzykiwania przez adnotacje i ogólnie żadnych adnotacji?

chciałbym sobie coś takiego w springu przejrzeć :P @jarekr000000 może ty?
jakiś github/prezentacje?

edytowany 3x, ostatnio: azalut
jarekr000000
@azalut: mam taki już całkiem spuchnięty (rozrósł się) na produkcji, ale niestety nie mogę niestety podać żródeł :-( (chociaż była dyskusja nad częsciowym opensourcowaniem). Mam tylko małe projekciki na githubie (na użytek szkoleń).
azalut
@jarekr000000: mozesz nawet podeslac te mniejsze projekciki: java/kotlin? domyslam się że to drugie?
Charles_Ray
To ja bym się zastanowił, czy w ogóle Spring to dobry wybór.
jarekr000000
Mi się ten okrojony, czysty spring sprawdza. Jest to po prostu zestaw bibliotek, ma co trzeba. Inaczej musiałbym sobie pokleić sam (np.: ktor + jooq). Dawno temu rozmawiałem na ten temat z Jurgene Hoellerem - oni w zasadzie przygotowali się na taki scenariusz przed springiem 5. Po to są ładnie wydzielone te wszystkie xxxtemplate, i dlatego spring w środku nie używa springa.
azalut
@Charles_Ray: nie stoję przed wyborem, ciekawi mnie jak to wygląda
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Tu masz czyjś - przynajmniej jest jakoś opisany:
https://github.com/making/vanilla-spring-webflux-fn-blank

ja zawsze wrzucam swojego ratponga, w którym jest nieco więcej kodu
https://github.com/javaFunAgain/ratpong

Tylko on nie używa spring Webfluxa, (tylko ratpacka),
ale z WebFluxem kod byłby prawie taki sam (byłby minimalnie lepszy , API jest fajniejsze niż to ratpackowe).
(tak czy siak, nie chciało mi się na springa przepisywać)


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

Walczę z połączeniem Mono i Either od kilku h i nie mogę za cholerę pożenić operacji pobrania i zapisu encji z wykorzystaniem webfluxa i vavra:

Kopiuj
data class Val1(val value: String) {
    companion object {
        fun of(str: String): Either<Error, Val1> {
            //
        }
    }
}

data class Val2(val value: String) {
    companion object {
        fun of(str: String): Either<Error, Val2> {
            //
        }

}

data class Entity(val id: String) {

}
    fun update(val1: Val1, Val2): Entity {
        //
    }

    fun dto(): EntityDto {

    }
}

interface Repo {

    fun load(id: String): Mono<Entity>

    fun save(entity: Entity): Mono<Entity>>
}

class Service {

    fun update(entityId: String, str1: String, str2: String): Mono<Either<Error, EntityDto>> {
        val val1: Either<Error, Val1> = Val1.of(str1)
        val val2: Either<Error, Val2> = Val2.of(str2)
        val entity: Mono<Entity> = repo.load(entityId)
        
        // TODO save i zwrocenie dtosa
    }
}

Ktoś chętny ?

edytowany 3x, ostatnio: Bambo
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2
Kopiuj
  fun update(entityId: String, str1: String, str2: String): Mono<Either<Error, EntityDto>> =
            repo.load(entityId).flatMap { entity ->
                Val1.of(str1).flatMap { val1 ->
                    Val2.of(str2).map { val2 ->
                        entity.update(val1, val2)
                        repo.save(entity)
                                .map { savedEntity ->
                                    Either.right<Error, EntityDto>(savedEntity.dto())
                                }
                    }
                }.mapLeft { Mono.just(Either.left<Error, EntityDto>(it)) }
                        .getOrElseGet { it }
            }

żeby to lepiej wygladało to

Kopiuj
fun Either.merge() =  this.getOrElseGet { it } 

 fun <E,T> Mono.left(v:T) = Mono.just(Either.left<E, T>(v))

jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
Charles_Ray
Nawet nie trzeba obfuskować xD
jarekr000000
Jest to poprawialne. Podałem jak zacząć. Niestety - wszystko co nie jest zbliżone do haskella jest by default nieczytelne i trzeba się natrudzić.
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

Ah jak ja uwielbiam te dyskusje :D Z tego forum się nauczyłem 100x więcej niż z reszty internetu i ksiażek :D

@jarekr000000 dzięki, ten malLeft naprawił sprawę, kombinowałem z samymi mapami i flatMapami.
Generalnie zdaję sobie sprawę, że takie przypadki pojawiają się często i występują pewnie w jeszcze zawiłej formie. Masz jakiś patent na łączenie tego czy po prostu kwestia posiedzenia z mapami i flatMapami ? Bo często dojście z opakowanym podwójnie w jakieś Mono i Eithery obiektem, a zakładam, że mogą się pojawić po3opakowane jest nietrywalnym problemem. Wczoraj nawet zacząłem to sobie na kartce rysować i opakowywać kółeczka w kwadraciki i romby XD.

Ułatwiłem sobie w ten sposób:

Kopiuj
enum class AppError {

    CANNOT_PARSE_DATE,
    CANNOT_PARSE_CURRENCY,
    CANNOT_PARSE_PRICE;

    fun <T> toEither() = Either.left<AppError, T>(this)

    fun <T> toMono() = Mono.just(toEither<T>())

}

I czy ta metode merge() nie powinna wyglądać tak:

Kopiuj
fun <T> Either<AppError, T>.merge() = this.mapLeft { it.toMono<T>() }
        .getOrElseGet { this.get() }

Bo ta Twoja to się nie kompiluje :D

EDIT: A nie tak nie może wyglądać bo ten get na końcu ..

Ale to poniżej się w ogóle nie kompiluje:

Kopiuj
fun <T> Either<AppError, T>.merge() = this.mapLeft { it.toMono<T>() }
        .getOrElseGet { it }

EDIT2
Czy testując jednostowko serwisy zwracające Mono<Either>> nie jest super błędem walić na mono block() i eitherze get() ? :D

edytowany 5x, ostatnio: Bambo
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Jak sobie radzić:

  • typealiasy pomagają,
  • używam w miarę możliwości tylko wyrażeń, instrukcje won, wtedy praktycznie każda linijka musi mieć jakiś typ i musi się to zgadzać, (prawie - bo czasem deklaruje zmienne (val))
  • wtedy ratuje mnie kompilator i podpowiadanie typów (show type hints)

Co do get i block w testach. Jest taka jedna zasada, że niby kod w testach nie może być gorszej jakości niż ten w normalnym kodzie,
skoro w normalnym kodzie get to błąd to i w testach też. Ale całkiem 100% nie jestem przekonany do tej zasady - za dużo kosztuje czasem i mi zniechęca ludzi do testów :-(

Co do block() to masz reactor-test i polecam, (choć nie jest idealny, ma zwodnicze api trochę),
Co do get() - to najlepiej by napisać asercje, matchery, własne na to (czyli mieć get w jednym wyjątkowym miejscu, we frameworku).
Ale prawda czasu jest taka, że zawsze powtarzam sobie, że trzeba by... a tymczasowo robię w testach Either.get()... i tak już zostaje.


jeden i pół terabajta powinno wystarczyć każdemu
danek
właściwie też nie do końca się zgadzam. get() to błąd w pewnym kontekście (czyli własnie w normalnym przetwarzaniu), a w innym (jak testy) już nie musi być. Nie ma co być purystą :p
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

Zrobiłem coś takiego:

Kopiuj
    fun <R> assertMonoEitherRight(mono: Mono<Either<AppError, R>>, asserts: (r: R) -> Unit) {
        assertNotNull(mono)
        val either = mono.block()
        assertTrue { either!!.isRight }
        asserts(either!!.get())
    }

    fun <R> assertMonoEitherLeft(mono: Mono<Either<AppError, R>>, asserts: (appError: AppError) -> Unit) {
        assertNotNull(mono)
        val either = mono.block()
        assertTrue { either!!.isLeft }
        asserts(either!!.left)
    }
edytowany 1x, ostatnio: Bambo
jarekr000000
Badzo Spoko. Ten AppError można dać jako generyczny parametr fun<E,R> ...
Bambo
Jakbyś jeszcze tylko pomógł z tą metodą merge(), żeby ten mapLeft + getOrElseGet ograć w jedną metodę .. od 2h kombinuję
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2

To merge wyżej pisałem oczywiście bez kodowania - wiec sie walnąłem - ma być:
fun <T> Either<T, T>.merge() = getOrElseGet { it }

Kluczowe jest <T,T> najpierw sprowadzamy lewą i prawą stronę do tego samego - a potem merge wybiera jedną z nich - czyli zostajemy z T.
(kocnept merge ukradłem ze ScalaZ - sprawdzał tam mi się).


jeden i pół terabajta powinno wystarczyć każdemu
Bambo
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Postów:779
0

@jarekr000000:
Dzięki, wszystko działa zajebiście :D napisałem już cały core pure FP, z ręcznym DI. Wygląda wszystkie zajebiście ! Robię demko, żeby propagować wiedzę w robocie, więc wstawię tu jak zrobię wsio w pełni.

Jeszcze tylko taka sprawa: wiem, że trzymasz kolekcje vavra bezpośrednio w encjach. Przy zapisie im memory wiadomo nie ma z tym problemu. A z jakimś mongo ? Próbowałem, ale przy odczycie encji coś się wywalało.

Robienie wartswy dbosów wydaje mi się średnim pomysłem.

edytowany 3x, ostatnio: Bambo
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
0

Mam vavra w modelu i czasem przeszkadza przy serializacji. Nie kojarzę jak to jest z mongo (za dawno coś w tym robiłem, nawet nie wiem czy używałem wtedy coś jak vavr).
Przy serializacji do json zwykle można podać własne serializaery.
Np. w takim Jacksonie używam tego:
https://github.com/vavr-io/vavr-jackson


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)