Azure Functions repræsenterer et paradigmeskifte i måden, udviklere skaber og implementerer applikationer på. I stedet for at tænke i monolitiske strukturer eller tungt vedligeholdte servermiljøer, kan man med Azure Functions arbejde med små, isolerede enheder af funktionalitet – nanoservices – der reagerer på hændelser og skalerer automatisk uden behov for manuel infrastrukturstyring.
Kernen i Azure Functions er dens triggers og bindings. En trigger definerer, hvordan funktionen aktiveres – det kan være alt fra en HTTP-anmodning, til en ændring i en database, en timer eller en meddelelse i en kø. Bindings gør det muligt for funktionen at interagere med andre Azure-tjenester uden at skrive eksplicit kode til det. Dette reducerer både kompleksitet og vedligeholdelsesomkostninger. Eksempelvis kan man via en binding automatisk læse fra en BLOB-storage, og sende output til en kø – alt sammen uden ekstra infrastrukturkode.
NCRONTAB-udtryk muliggør præcis planlægning af funktioner, som f.eks. at køre noget hver mandag kl. 9:00. Dette er særligt nyttigt ved implementering af periodiske vedligeholdelsesopgaver eller planlagte integrationer med eksterne systemer.
Azure Functions findes i forskellige versions- og sprogunderstøttelser, og valg af versionsmodel har betydning for funktionens kompatibilitet, vedligeholdelse og ydeevne. Det er vigtigt at vælge en stabil version, som understøtter de nødvendige features, men samtidig sikre, at den ikke er forældet i forhold til platformens livscyklus.
Hostingmodellerne spænder fra Consumption Plan, hvor man kun betaler for det faktiske ressourceforbrug, til Premium og Dedicated Plans, som tilbyder mere kontrol og højere ydeevne. Valg af plan bør altid baseres på applikationens forventede belastning og responstidskrav.
For lokal udvikling og test tilbyder Microsoft værktøjer som Azurite – en lokal emulering af Azure Storage, samt Azure Functions Core Tools, der tillader lokal kørsel, debugging og test af funktionerne. Dette minimerer afhængigheden af en aktiv cloudforbindelse under udviklingsfasen og øger udviklingshastigheden.
Sikkerhed er indbygget gennem forskellige autorisationsniveauer. En funktion kan gøres offentlig, beskyttet med nøgle, eller sikres via OAuth 2.0 – afhængigt af anvendelsestilfældet. Ved applikationer med høj følsomhed bør man aktivt styre nøglerotation og implementere adgangsbegrænsning.
Et vigtigt aspekt i større løsninger er dependency injection, som Azure Functions understøtter. Dette muliggør genbrug af komponenter, styring af livscyklus og separation af ansvar. Det gør funktionerne mere testbare og modulære – en nødvendighed i kompleks forretningslogik.
Til udvikling og implementering kan man benytte Visual Studio 2022, Visual Studio Code eller kommandolinjeværktøjet func. Hver metode har sine fordele –
Hvordan håndtere CORS-fejl i webtjenester med JavaScript-klienter
Når man arbejder med moderne webapplikationer, der involverer kommunikation mellem forskellige domæner eller porte, er CORS (Cross-Origin Resource Sharing) en vigtig mekanisme at forstå. CORS-politikken gør det muligt for webtjenester at kontrollere, hvilke domæner der har tilladelse til at få adgang til deres ressourcer. Hvis en JavaScript-klient på en webside forsøger at få adgang til en ressource fra en anden oprindelse (f.eks. et API, der kører på en anden port), kan det føre til fejl, som kan være svære at forstå for udviklere, især når man arbejder med forskellige udviklingsmiljøer.
Et eksempel på denne situation kan opstå, når vi forsøger at kommunikere mellem en webapplikation, der kører på port 5092, og en webtjeneste, der kører på port 5091. Ved forsøg på at hente data fra webtjenesten får man en fejl i udviklerværktøjerne i browseren, som lyder: "Access to XMLHttpRequest at 'https://localhost:5091/api/products/man' from origin 'https://localhost:5092' has been blocked by CORS policy". Dette er et klart tegn på, at browseren har afvist anmodningen på grund af, at den oprindelige og den målrettede server kører på forskellige porte og dermed ikke deler den samme oprindelse.
Når en JavaScript-klient forsøger at oprette forbindelse til en webtjeneste, der ikke er konfigureret til at tillade anmodninger fra en anden oprindelse, vil browseren automatisk blokere anmodningen af sikkerhedsmæssige årsager. I den nævnte situation resulterer anmodningen i en fejlkode, der viser, at der mangler CORS-headeren "Access-Control-Allow-Origin", som er nødvendigt for at tillade anmodninger fra en anden oprindelse.
CORS-fejl opstår, fordi browsere ikke tillader scripts at få adgang til data fra en anden oprindelse, medmindre serveren eksplicit tillader det ved at sende de nødvendige CORS-header. For at løse dette problem skal udvikleren sikre, at webtjenesten, som i dette tilfælde er "Northwind.WebApi.Service", er konfigureret korrekt til at tillade cross-origin-anmodninger.
I det beskrevne eksempel starter webtjenesten på port 5091 og webklienten på port 5092, hvilket betyder, at de har forskellige oprindelser. Derfor skal serveren konfigureres til at tillade anmodninger fra oprindelsen "https://localhost:5092". Dette kan gøres ved at tilføje den relevante CORS-konfiguration i serverens middleware. CORS-politikken skal konfigureres i serverens kode for at angive, hvilke oprindelser der er tilladt at få adgang til ressourcerne. I .NET kan dette gøres ved hjælp af AddCors-metoden i Startup.cs eller ved at bruge den nye Program.cs-konfiguration, hvor man definerer hvilke URL'er der har tilladelse til at interagere med API'et.
Når CORS-politikken er korrekt konfigureret, vil browseren tillade anmodninger fra andre oprindelser, og JavaScript-klienten vil kunne hente data fra webtjenesten uden fejl.
Det er vigtigt at bemærke, at CORS-politikken ikke kun er et udviklingsmæssigt fænomen. Det er et vigtigt sikkerhedslag, der beskytter mod ondsindet aktivitet som Cross-Site Request Forgery (CSRF), hvor angribere forsøger at udnytte tilladelser, der er givet til en oprindelse. Derfor bør CORS-politikken implementeres forsigtigt, og kun de nødvendige oprindelser bør tillades adgang til API'et. Dette kan omfatte specifikation af bestemte domæner, som har tilladelse til at gøre API-anmodninger, fremfor at bruge en alt for åben tilgang som "*", hvilket tillader alle oprindelser.
Når du arbejder med et .NET-webclientprojekt som dette, kan det være nødvendigt at justere HTTP-klientens konfiguration for at kunne kommunikere med webtjenesten. En typisk måde at gøre dette på i .NET er at bruge HttpClient og tilføje nødvendige headers for at sikre, at serveren kan forstå og håndtere anmodningen korrekt. Dette kræver, at du angiver den korrekte Accept header og eventuelt andre konfigurationsparametre som f.eks. BaseAddress for at sikre, at anmodninger sendes til den rette server.
Det er også vigtigt at bemærke, at denne problemstilling kun opstår, når klienten og serveren kører på forskellige oprindelser. Hvis både klienten og serveren kører på samme oprindelse (samme domæne og port), vil CORS-politikken ikke blive udløst, og anmodningen vil blive accepteret uden problemer.
Som en ekstra bemærkning er det vigtigt at forstå, at CORS-fejl ikke nødvendigvis indikerer en fejl i webtjenestens funktionalitet. Det er snarere et tegn på en sikkerhedsmæssig barriere, der beskytter mod utilsigtet adgang. Derfor er fejlhåndtering af CORS-anmodninger et vigtigt aspekt at tage højde for i webudvikling, specielt når man arbejder med klient-server-arkitektur, der involverer flere domæner eller porte.
Hvordan versionering og CRUD-operationer fungerer i OData-tjenester
OData-tjenester tilbyder en kraftfuld og fleksibel måde at eksponere data på via internettet, og giver både fleksibilitet i forespørgsler og effektiv håndtering af CRUD-operationer. Når man arbejder med OData, er det vigtigt at forstå både, hvordan man versionerer OData-kontrollere, samt hvordan man implementerer funktioner til oprettelse, læsning, opdatering og sletning af data.
OData-metoden, der bruges i produktercontrolleren, returnerer faktisk ikke hele produkt-tabellen, men derimod et IQueryable-objekt. Det betyder, at det returnerer en LINQ-forespørgsel, som endnu ikke er eksekveret. Denne forespørgsel kan dog udvides af OData med filtre, sortering, projektioner og andre operationer. Det betyder, at først når disse ekstra elementer er blevet anvendt på forespørgslen, bliver den eksekveret, og resultatet bliver returneret til klienten. Denne fleksibilitet gør OData-tjenester yderst effektive i deres overgang fra query-sprog til LINQ og videre til SQL-udtryk.
For at kunne håndtere fremtidige versioner af en OData-model og sikre bagudkompatibilitet, kan man bruge OData URL-præfikser, der inkluderer en versionsnummer. Dette gøres ved at definere versioner i API-ruterne, som kan håndtere flere versioner af samme model. For eksempel kan du i dit OData-projekt definere en rute som "catalog/v{version}", hvor versionen kan specificeres som en parameter. Dette gør det muligt for klienten at anmode om data fra en specifik version af API'et, og controlleren kan derefter ændre sin opførsel baseret på den angivne version.
Når det kommer til CRUD-operationer, er OData ikke kun et værktøj til at forespørge data, men også til at manipulere dem. For at kunne tilføje nye produkter via en POST-anmodning, kan man oprette en handling i controlleren, der modtager et produktobjekt, tilføjer det til databasen og gemmer ændringerne. For at opdatere eller slette data kan man implementere yderligere metoder, der håndterer PUT- og DELETE-anmodninger.
For at teste en CRUD-operation kan man bruge et HTTP-klientværktøj, som Visual Studio Code's RestClient. Når man sender en POST-anmodning til en OData-tjeneste for at tilføje et produkt, returnerer tjenesten et JSON-svar, der bekræfter, at objektet er blevet oprettet korrekt i databasen. Dette svar inkluderer alle egenskaber af det oprettede objekt, og klienten kan bruge denne information til at opdatere brugergrænsefladen eller vise en succesmeddelelse.
Når man arbejder med klienter, der interagerer med OData-tjenester, skal man forstå, hvordan man opbygger korrekte HTTP-anmodninger for at hente data. Hvis en klient ønsker at finde produkter, hvis navne starter med en specifik streng, kan de sende en GET-anmodning med en forespørgsel som f.eks. $filter=startswith(ProductName, 'Cha'). OData returnerer dataene i et JSON-dokument, som indeholder de produkter, der matcher filteret.
For en bedre brugeroplevelse og effektiv implementering af OData-tjenester skal man tage højde for flere faktorer. Det er vigtigt at have en klar plan for versionering og API-ændringer, så klienter kan håndtere forskellige versioner af tjenesten uden problemer. Derudover er det afgørende at forstå, hvordan data hentes og manipuleres effektivt via OData, for at kunne udnytte denne teknologi optimalt. Med den rette implementering kan OData-tjenester blive en meget kraftfuld måde at opbygge og vedligeholde webtjenester, der giver fleksible og effektive API'er til dataudveksling.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский