Модул 12. Интернет програмиране
Съдържание
Internet and Sockets
HTTP
Introduction to HTML
HTTP Server
Introduction to MVC
Database
Template Language
State Management
Authentication and Authorization
Security
REST API
Consuming REST API
Deployment
Exam Preparation
1. Internet and Sockets
1. Какво ще направим?
Ще създадем конзолно приложение - чат, като изпратените съобщения чрез клиента ще получаваме съответно на сървъра, а комуникацията ще се осъществява посредством сокети.
2. Архитектура на проекта
Проектът, ще се състои от два подпроекта, като съответно единият ще служи за нашият socket сървър, а другият за socket клиент. Ще поставим и двата проекта под един solution на име ChatServer.

За да се запази проекта в прост вид и да се наблегне върху новите знания за сокети, а не върху шаблони за дизайн, ще поставяме нашият код директно в Main() методите.
3. Сървър
Първо ще изградим имплементацията на нашия сървър. За такъв ще използваме конзолно приложение, което отваря нов socket, като се свързва на определен порт и започва да слуша за приидващи данни.
Приложението, което ще създадем ще работи само локално. Нека започнем с вземането на адрес данните за хоста ни. Ще използваме статичният клас Dns и в частност метода му GetHostName(), намиращ се в библиотеката System.Net, за да вземем името на текущия хост. След като разполагаме с името на хоста ще използваме метода GetHostEntry(), с параметър името на хоста, на същия клас, за да разглеждаме хоста като IPHostEntry – клас, който служи като контейнер за информация относно интернет хост адрес.
Тази информация ще използваме, за да вземем IPAddress-а на хоста (от списъка с адреси, който IPHostEntry-то съдържа).
IP адресът, който имаме, ще използваме за да създадем нова крайна точка (endpoint), към който трябва да се обърне клиента. Нека създадем нова иснтанция на класа System.Net.IPEndPoint, като за параметри ще подадем IP адреса и порта, към който искаме да се свържем. Нека изберем за порт 11000.
Какво имаме досега:
След като сме създали нашия endpoint е време да създадем и сокет, който да слуша на този IP адрес. Ще създадем нова инстанция на класа Socket, който се намира в библиотеката System.Net.Sockets. За тип на сокета ще изберем Steam – това е типът, който поддържа надеждни, двустранни, базирани на връзка (connection-based) байтови потоци. Този тип сокети използват TCP.
Нека свържем сокета си към крайната ни точка, след което окажем на сокета да започне да слуша. Параметърът backlog, който получава метода Listen() е максималната дължина на опашката от висящи връзки. С масив от байтове ще създадем нашия буфер – размера на данни, който ще получаваме. Нека масивът ни е с размер 1024 байта. Върху вече създаденият ни сокет, който слуша за новоизискани връзки, ще създадем нов сокет, който приема първата заявка за свързване от вече споменатата опашка от висящи връзки.
С два вложени безкрайни цикъла ще проверяваме дали е пристигнало съобщение по нашия сокет. Ако това се е случило, ще го изпишем на екрана (конзолата). За край на съобщението ще приемаме , което е съкращение стоящо за end of file. Текста получаван по сокета ще четем като ASCII енкодиран. Начинът, по който сокета ще поучава данните е чрез метода си Receive(), а като параметър се подава буфер – ние вече разполагаме с такъв.
Край на изпълнението на програма ще слага въвеждането на съобщение exit, за което ще проверяваме и при което ще прекъсваме действието цикъла си. Преди да направим това обаче е нужно да затворим сокета си, което става чрез методите Shutdown() – с параметър SocketShutDown.Both (затваря както за четене, така и за писане) и Close(). Преди да спрем изпълнението на сървъра ще изпишем на конзолата Goodbye.
Как изглежда това:
С това имплементацията на нашия сокет сървър е завършена.
4. Клиент
След като разполагаме със сокет сървър, е време да изградим и нашия сокет клиент.
По аналогичен начин ще създадем сокет, но този път той ще служи за изпращане на данни.
В този случай ще използваме съответните методи на сокета за изпращане на данни. Съобщението ще четем от конзолата като потребителски вход, след което ще го преобразуваме в масив от байтове, като накрая на всяко съобщение ще долепваме , за да окажем, че това е краят му (при натискане на enter).
Методът на сокета .Connect(), приемащ крайна точка осъществява връзката със сървъра ни, а методът Send(), приемащ масив от байтове изпраща съобщението към него. Отново ако съобщението е exit изпълнението на програмата приключва.
Как излежда това:
5. Как да използваме приложението
За да използваме приложението, трябва сървърът да е пуснат преди клиента. В противен случай връзката не може да бъде осъществена. За целта ще се възползваме от инструментите на Visual Studio и ще настроим при старт на програта да се пускат едноврменно и сървърът и клиентът, като сървърът да е първи.

От solution explorer-а ще изберем опцията Set StartUp Projects…, която можем да открием в падащото меню след десен клик върху името на solution-а ни.

От появилото се меню ще изберем опцията Multiple startup projects, и ще създадем последователността, в която проектите трябва да стартират – в нашия случай отгоре ще сложим сървърът. За Action съответно изберете start/start without debugging.

При стартиране на проекта би трябвало да се отворят две конзолни приложения – нашият сървър и нашият клиент. В клиентското приложение ще се изпише адреса, на който се е свързал сокета ни, както и съответния порт. На този етап всяко съобщение ще бъде изпратено от клиента на сървъра. Нека тестваме:

За да приключи изпълнението, както на нашия сървър, така и на нашия клиент въвеждаме като съобщение “exit”. Съответно преди да се затвори сървъра изчиства изпратените съобщения и изписва “Goodbye”.

Това е финалната стъпка – вече разполагаме със собствен чат сървър.
2. HTTP протокол
Инсталиране на необходимия софтуер
Postman
Можете да изтеглите Postman от тук: https://www.getpostman.com/downloads/
Направете основни HTTP заявки
1. Kinvey
Трябва да въведем следната заявка "GET" в Postman: https://baas.kinvey.com/appdata/kid_S1rDXLP4N
Първо, отидете на Authorization в Postman и изберете "Basic Auth". Въведете потребителско име: "guest" и парола: "guest":
Заявка: 
Отговор: 
2. Вземете всички песни
Отидете на Authorization в Postman и изберете "Basic Auth". Въведете потребителско име: " guest" и парола: " guest". След това изброяването на всички песни трябва да е лесно със следната заявка: https://baas.kinvey.com/appdata/kid_S1rDXLP4N/songs
Заявка: 
Отговор: 
3. Създайте нова песен
Отново отидете на Authorization в Postman и изберете " Basic Auth". Въведете потребителско име: " guest" и парола: " guest". Сега трябва да създадем нова песен:
Заявка: 
Отговор: 
4. Изтрийте песен
Сега нека изтрием новосъздадената песен. Заявка "DELETE": https://baas.kinvey.com/appdata/kid_S1rDXLP4N/songs/postId. PostId може да се намери в JSON отговора на предишната задача:

Заявката "DELETE" трябва да генерира отговор, който ни казва колко публикации сме изтрили:

Сега, когато получим всички песни, можем да забележим, че песента "Back In Black" не е в списъка.
5. Редактиране на песен
Редактирайте песен с PUT заявка. https://baas.kinvey.com/appdata/kid_S1rDXLP4N/songs/5c5adb00c892215f50bd6761
Променете следните колони:
name: "Dani California"
album: "Stadium Arcadium"
time: "4:43"
year: "2006"
добавете допълнителна колона: genre: "Rock"
Заявка: 
Отговор: 
6. Вход
Отново отидете на Authorization в Postman и изберете "Basic Auth" и въведете потребителско име: "guest" и парола: "guest".
Влизането се извършва със заявка "POST" на следния URL адрес: https://baas.kinvey.com/user/kid_S1rDXLP4N/login
Също трябва да изпратите своите идентификационни данни чрез JSON тялото:: 
След успешно влизане трябва да получите следния отговор: 
Запазете authtoken, защото ще ви е необходим за крайната задача.
7*. Бонус: Излез от профила си
И накрая, трябва да излезем от приложението. Променете Authorization на "No Auth". За целта трябва да изпратим заявка "POST" на следния URL адрес: https://baas.kinvey.com/user/kid_S1rDXLP4N/_logout
Спомняте ли си този дълъг authorization token? Сега трябва да го копираме и да го поставим в секцията "Headers" на Postman: 
След като щракнете върху "Send", отговорът трябва да бъде празен. Ако го направите отново, трябва да предизвика грешка. 
8**. Реализация на GET и POST в C#
GET
GET async
POST
POST async
3. Introduction to HTML
1. Основи на HTML
Какво е HTML?
HTML = HyperText Markup Language
Нотация за описание: структура на документа, форматиране (презентация)
Tаговете предоставят метаинформация за съдържанието на страницата и определят нейната структура
Един HTML документ се състои от много тагове, които се влагат
HTML терминология
Тагове = най-малкият елемент в HTML
Атрибути = свойсвтвата на таговете - размер, цвят и т.н
Елементи = комбинация от отварящ, затварящ таг и атрибути
Вашата първа HTML страница
Семантични тагове в HTML5
В HTML5 има семантични тагове за оформление: <header>, <footer>, <nav>, <aside>, <section>.
2. Често използвани тагове
Заглавия
Заглавията помагат при структурата на страниците, както в Microsoft Word. Html има шест различни HTML заглавия:
<h1> определя най-важното заглавие.
<h6> определя най-малко важното заглавие.
Параграфи
Тагът <p> дефинира параграф. Тагът <br/> дефинира нов ред.
Булети и номерирани списъци
Номериран списък
Хипервръзки
Локални хипервръзки
Локалните връзки могат да сочат към една и съща страница.
Снимки
Таблици
Атрунути на таблицата
Обединяване на колони
Обединяване на редове
3. Формуляри в HTML
HTML формите позволяват на потребителя да попълва данни и да ги изпраща до сървъра.
Полетата за вход може да съдържат текст, номер, дата, радио бутон, ...
Създаване на форма за контакт:
Tипове вход
Падащ списък
Падащите списъци се дефинират с тага <select>
<option> елементите определят опции, които могат да бъдат избирани
Тексови полета
Многоредови полета за въвеждане на текс
Дефинира се с тага <textarea>
Атрибутите row и col дефинират колко реда и колони ще обхване текстовата област
4. CSS
CSS определя стила на HTML елементите:
Определя шрифтове, цветове, полета, размери, позициониране ...
CSS се декларира в следния формат: свойство:стойност
Вграденият CSS дефинира правила за форматиране на определен HTML елемент:
Шрифтове - семейство шрифтове, размер и цветове
color: определя цвета на буквите
font-family: трябва да съдържа няколко шрифта. Ако браузърът не поддържа първия, той ще опита следващия
font-size: задава размера
Блокови елементи
Блокови елементи (<div>;<h1>;<p>): Винаги започвайте на нов ред. Заемат цялата налична ширина.
<div> елемента: често се използва като контейнер за други HTML елементи
Вградени елементи
Вградени елементи (<span>;<a>;<img>): Не започват на нов ред. Заемат само толкова ширина, колкото е необходима.
<span> елемент: Често е използван за контейнер за текст
Граници и фонове
border: определя типа, дебелината, цвета
border-radius: закръгля граничните краища
background: задава фона
Външни отстояния
Използва се за генериране на пространство около елементи
margin свойсвото задава размера на празното пространство извън границата
Вътрeшни отстояния
Използва се за генериране на пространство около съдържанието
Свойството padding задава размера на празното пространство вътре в границата
Отделяне на съдържание и оформление
.class = избира група елементи с посочения клас
#id = избира уникален елемент
tag = избира всички посочени тагове
document.html
style.css
4. HTTP Server
С помощта на този документ, вие ще създаде малък HTTP сървър, който изпраща и приема заявки. В последствие ще създадаем малък MVC Framework, който ще работи с нашият HTTP сървър.
1. Архитектура
Първо нека да създадем архитектурата на нашият проект. Създайте нов Solution и го кръстете MiniServer. Добавете два проекта към него:
MiniServer.HTTP
MiniServer.WebServer
2. MiniServer.HTTP Архитектура
HTTP проекта ще съдържа всички класове (и техните интерфейси), които ще бъда изпозлвани да имплементираме HTTP комуникацията с TCP Link между клиента и нашият сървър. Можем да работим само с низове и байт масиви, но ще следваме добрите практики и ще го направим кода да бъде лесно четим и преизползваем.
Създайте следните папки в проекта MiniServer.HTTP

Както виждате архитектурата на папките е много добре разделена. Нека сега да започнем със създаването на класовете.
Common папка
Common папката, ще съдържа класове, които се изпозлват в целият проект. Ще имаме два класа: GlobalConstants и CoreValidator.
GlobalConstants
Създайте статичен клас GlobalConstants, който ще бъде използван за споеделните константи:
Това са единствените константи, от които имаме нужда засега.
CoreValidator
Създайте клас CoreValidator, който ще има два метода, за проверка за null стойности или празни стрингове:
Enums папка
Enums папката ще съдържа enumerations. Има два енъма, от които сървърът ще се нуждае: HttpRequestMethod и HttpResponseStatusCode.
HttpRequestMethod
Създайте Enum, с името HttpRequestMethod. Той ще дефинира, метода ,които сървъра получава
Нашият сървър, ще поддържа само GET, POST, PUT и DELETE и заявки. Нямаме нужда от по-сложни заявки засега.
HttpResponseStatusCode
Създайте Enum, с името HttpResponseStatusCode. Той ще дефинира статус кода от отговора на нашият сървър. Този Enum, ще съдържа стойности, които са стутусите и цели числа, които ще представляват статус кода.
За сега нашият малък сървър, няма нужда да съдържа всички други статус кодове. Тези достатъчно сървъра и клиента да си комуникират.
Exceptions папка
Exceptions папката ще съдържа класове, които отговорят за правилното менажиране на грешките в сървъра. За начало ще имаме класа, които ще отговарят за грешките: BadRequestException и InternalServerErrorException. Тези грешки, ще помагат, така, че сървъра винаги да връща отговор, дори в случай на Runtime Error.
Сървърът първо ще хваща грешки, които са от тип BadRequestException. Ако хване грешка от този тип, сървъра трябва да върне 400 Bad Request Response и съобщение са грешката.
Всички други грешки ще бъдат от тип InternalServerErrorException или от базовия клас "Exception". Ако прихванем една от тези грешки, сървъра трябва да върне a 500 Internal Server Error" и съобщение за грешката.
BadRequestException
Създайте клас, който се казва BadRequestException. Тази грешка ще бъде хвърлена, когато сървъра не успее да парсне HttpRequest, като Unsupported HTTP Protocol, Unsupported HTTP Method, Malformed Request и т.н. Класът трябва да наследява, Exception класа и трябва да има съобщение по подразбиране: The Request was malformed or contains unsupported elements.
InternalServerErrorException
Създайте клас, който се казва InternalServerErrorException. Тази грешка ще бъде хвърлена, когато не се е предполагало сървъра да се справи с нея. Класът трябва да наследява, Exception класа и трябва да има съобщение по подразбиране: The Server has encountered an error.
Extensions папка
Extensions папката, ще съдържа extension методи, които ще ни помагат в разработката на нашият сървър. Ще има един клас StringExtensions.
StringExtensions
В този клас, имплементирайте низ extension метод, който се казва Capitalize(). Той трябва да направи първата буква главна и всички други малки.
Headers папка
Headers папката, ще съдържа класове и интерфейси, които ще съхраняват данни за HTTP Headers на заявката и отговора.
HttpHeader
Създайте клас, който се казва HttpHeader. Той ще съхранява данните за HTTP Request/Response Header.
Пропъртито "Key", ще се използва за името на Header-a и пропъртито "Value", ще съдържа стойността. Имаме и в помощ "ToString()" метода, който ще връща добре форматиран и готов за използване Header.
IHttpHeaderCollection
Създайте интерфейс, който се казва IHttpHeaderCollection, който ще опише действията на "Repository-like object" за HttpHeaders.
HttpHeaderCollection
Създайте клас, който се казва HttpHeaderCollection, който имплементира IHttpHeaderCollection интерфейса. Този клас е като "Repository". Трябва да има Dictionary колекция на всички Headers и трябва да имплементирате всички методи на интерфейса.
Имплементирайте всеки един от тези методи със следните функционалности:
AddHeader() = Добавя Header-a. в речника с ключ – ключа на Header-a и стойност самият Header.
– ContainsHeader() = Главна причина да използва Dictionary. Позволява ни бързо търсене. Трябва върнем boolean, в зависимост от това дали колекцията съдържа даденият ключ.
GetHeader() = Връща Header-a от колекцията с дадения ключ. Ако не съществува такъв Header, метода трябва да върне null.
ToString() = Връща всички Headers, като низ, разделени с нов ред - ("/r/n") или Environment.NewLine
Responses папка
Responses папката ще съдържа класове и интерфейси, които съдържат и манипулират информация за "HTTP Responses".
IHttpResponse
Създайте интерфейс, който се казва IHttpResponse и ще се съдържа следните пропъртита и методи:
HttpResponse
Създайте клас, който се казва HttpResponse и имплементира IHttpResponse интерфейса.
Както виждате HttpResponse съдържа: StatusCode, Headers, Content и т.н. Това са единствените неща, от които ние се нуждаем за сега. HttpResponse се инициализира с обект с Null ли по подразбиране стойности.
Сървърът получава Requests в текстов формат и трябва върне Responses в същият формат.
Репрезентацията на низа от HTTP Responses са в следният формат:
ЗАБЕЛЕЖКА: Както вече знаете, съдържанието (Response body) не е задължително.
Сега, докато изграждаме нашият HttpResponse обект, може да присвоим стойност за нашият StatusCode или може да го оставим за напред. Най-често ще присвояваме стойностите чрез конструктора.
AddHeader() метод
We can add Headers to it, gradually with the processing of the Request, using the AddHeader() method. Можем добавяме Headers, като използваме AddHeader() метода.
Другите пропъртита, StatusCode и Content могат да бъдат присвоени стойности от "външният свят", като използват публичните им сетъри.
Сега нека да видим ToString() и GetBytes() какво правят.
ToString() метод
ToString() метода формира Response реда – този ред съдържа протокола, статус кода, статус и Response Headers, като завършва с празен ред. Тези пропъртита са съединени в един низ и върнати в края.
И точно сега се нуждаем от GetBytes() метода.
GetBytes() метод
And with that we are finished with the HTTP work for now. We can proceed to the main functionality of the Server.
GetBytes() метода конвертира резултата от ToString() метода до byte[] масив, и долепя към него Content bytes, затова формираме целият Response до байт формат. Точна това, което трябва да изпратим до сървъра.
И вече приключихме с работата по нашият HTTP сървър за сега.
Requests папка
Сега е време да съберем всичко написано до момента в главните функциониращи класове.
Requests папката ще съдържа класове и интерфейси за съхранение и манипулиране данни за HTTP заявките.
IHttpRequest
Създайте интерфейс, който се казва IHttpRequest, който ще описва поведението на Request обекта.
HttpRequest
Създайте клас, който се казва HttpRequest, който имплементира IHttpRequest интерфейса. Класът трябва да имплементира и методите на интерфейса.
Както виждате HttpRequest, съдържа: Path, Url, RequestMethod, Headers, Data. Тези данни идвам променлива requestString, която се подава в констуктора. Това е начина, по които HttpRequest ще се инициализира. requestString ще изглежда по този начин:
ВНИМАНИЕ: Както вече знаете, че body parameters не за задължителни. Нека да разбием една нормална заявка и да видим как тя трябва да се мапне към нашите пропъртита.
Сега е време да имплементираме повече логика, което означава много методи, ако искаме да спазваме принципите за High-Quality Code. Имплементирайте следните методи.
ParseRequest() е метода откъдето започва всичко:
Нека да видим как изглежда той:
Както виждате requestString е разделен на нови редове в масив. Взимаме първият ред (The Request Line) и го разделяме. След това следват серия от проверки и присвояване не стойности към пропъртита.
Ще се наложи вие да имплементирате тези методи. Разбира се, ще ви бъдат дадени насоки, как да се справите с тях.
IsValidRequestLine() метод
Този метод проверя дали, разделеният requestLine съдържа точно 3 елемента и също така дали последният елемент е равен на "HTTP/1.1". Метода връща булев резултат.
IsValidRequestQueryString() метод
Този метод се използва в ParseQueryParameters() метода. Проверява дали Query низа е NOT NULL или празен и също така дали има поне един или много queryParameters.
ParseRequestMethod() метод
RequestMethod присвоя стойността, като преобразуваме първият елемент от разделеният requestLine.
ParseRequestUrl() метод
Url присвоява стойността от вторият елемент на разделеният requestLine.
ParseRequestPath() метод
Path присвоява стойността, като разделим Url и вземем само пътя от него.
ParseHeaders() метод
Пропускаме първият ред от requestLine и обхождаме всички останали редове, докато не стигнем празен ред. Всеки ред представлява header, който трябва да бъде разделен и преобразуван към правилният тип. След това информацията от низа е прехвърлена към HttpHeader обекта и е добавен към Headers пропертито на Request. Хвърлете BadRequestException, ако Host липсва след преобразуването.
ParseQueryParameters() метод
Извадете Query низа, като разделите "Request's Url" и вземете само query от него. След това разделете Query низа в различни параметри и го прехвърлете към "Query Data Dictionary". Валидирайте Query низа, като извикате IsValidrequestQueryString() метода. Ако в Request's Url липсва Query низа, не предприемайте действия. Хвърлете BadRequestException, ако Query не е валиден.
ParseFormDataParameters() метод
Разделете Request's Body в различни параметри и го добавате към Form Data Dictionary. Не предприемайте действия, ако Request не съдържа тяло.
ParseRequestParameters() метод
Този метод извиква ParseQueryParameters() и ParseFormDataParameters() методите. Това е просто wrapping метод.
Ако сте имплементирали всички правилно, би трябвало да преобразувате дори и много сложни заявки без проблем.
3. MiniServer.WebServer Архитектура
WebServer проекта ще съдържа информация за класовете, които изграждат връзка с TCP. Тези класове ще комуникират с класовете от HTTP Project. Проекта ще изнася няколко класа, които ще служат за външния свят, за да създаваме приложния.
Създайте следните папки в проекта MiniServer.WebServer

Results папка
Results папката ще съдържа няколко класа, които са наследени от HttpResponse класа. Тези класове, ще използват за имплементираме прости приложения с MiniServer. Трябва да създадем три класа вътре: TextResult, HtmlResult и RedirectResult.
TextResult
Създаден е така, че да връща текст, като отговор. Трябва да има Content-Type и header text/plain.
HtmlResult
Създаваме този клас, да връща HTML в себе си. Така чрез този клас, ние можем да върнем HTML или просто съобщение. Трябва да има Content-Type и header text/html.
RedirectResult
Този клас, не трябва да има Content. Единствената задача е да бъде пренасочен. Този Response има локация. Статуса трябва да бъде SeeOther.
Routing папка
В папката, ще съдържа логиката за рутиране и конфигурация на сървъра. Ще съдържа един интерфейс и един клас: IServerRoutingTable and ServerRoutingTable.
Този клас съдържа големи колекции от насложени асоциативни масиви, които ще се използват за рутиране.
Това е главният алгоритъм за Request Handling. Request Handler се конфигурира, като настройва Request Method и Path на заявката. Handler сам по себе си е функция, която приема Request параметър и генерира Response параметър.
Ще видим по-надолу в примерите как работи.
Server клас
Server класа е обвивка за TCP connection. Използва TcpListener , за да запише връзката с клиента и да я подаде на ConnectionHandler, която го изпълнява.
Конструкторът се използва, за да бъде инициализиран Listener и RoutingTable.
Този метод се използва за процеса на слушане. Процесът трябва да бъде асинхронен, за да подсигури функционалността, когато двама клиенти изпратят заявка.
Listen() метода е главният процес при свързване с клиента.
Както виждате, ние създаваме нов ConnectionHandler, за всяка нова връзка и я подаваме на ConnectionHandler, заедно с routing table, така че заявката да бъде изпълнена.
ConnectionHandler клас
ConnectionHandler е клас, който произвежда връзката с клиента. Приема връзката, изважда заявката, като низ и минава процес през routing table, като я изпраща обратно на "Response" в байт формат, чрез TCP link.
Конструктора се използва, за да се инициализира socket и routing table.
ProcessRequestAsync() метода е асинхронен метод, който съдържа главната функционалност на класа. Използва и други методи да чете заявки, да ги обработва и да създава Response, Който да бъде върнат на клиента и най-накрая да затвори връзката.
ReadRequest() метода е асинхронен метод, който чете байт данни, от връзката с клиента, изважда низа от заявката и след това го обръща в HttpRequest обект.
HandleRequest() метода проверява ако routing table има handler за дадената заявка, като използва Request's Method и Path
Ако няма такъв handler Not Found отговор е върнат.
Ако има такъв handler, функцията е извикана и резултата е върнат.
Това е финалният вид на нашия ConnectionHandler и WebServer проект.
4. Hello, World!
Създайте трети проект, който да се казва MiniServer.Demo. Той трябва да реферира и двата проекта MiniServer.HTTP и MiniServer.WebServer.
Създайте следните класове:
HomeController клас
HomeController класа трябва да има един метод – Index(), който да изглежда по този начин:
Launcher клас
Launcher класа трябва да съдържа Main метода, който инстанциира Server и го конфигурира да се справя със заявките, като използва ServerRoutingTable.
Конфигурирайте само пътя "/", като използва ламбда функция, която извиква HomeController.Index метода.
Сега стартирайте MiniServer.Demo проекта и трябва да видите това на конзолата, ако всичко е наред:

Отворете браузъра и отидете на localhost:8000 и трябва да видите това:

Поздравления! Завършихте първото си приложение с MiniServer.
5. Introduction to MVC
След като изградихме нашият HTTP Server е време да го впрегнем в действие и да видим как работи.
1. Архитектура
Първо нека да създадем архитектурата на нашият проект. Към вече съществуващият sln. добавете нов проект и го кръстете IRunes.App
2. IRunes.App Архитектура
IRunes.App ще съдържа логиката за контролер, моделите, ресурсите и html файловете. Ще имаме един клас "Launcher" от където ще започва нашата логика.

Нека да започнем с папката Controllers:
Controllers папка
Controllers папката, ще съдържа нашите контролери, благодарение на тях ние ще можем да обработваме данни, да пренасочване ресурси и т.н. За сега ще създадем един клас, който ще се казва BaseController.
BaseController клас
Създайте абстрактен клас BaseController. Това ще бъде базовият клас за всички контролери.
ViewData пропърти То ще помогне, когато искаме да визуализираме данни. Първо ще ги добавим в този асоциативен масив и после ParseTemplate метода ще ги обработи.
ParseTemplate метод Този метод ще обходи целият речник и там, където засече @Model. в нашият html, ще го замени с данните, които искаме. Този метод ще стане по-ясен, когато стигнем до описването на html файлове.
View метод Това е най-сложният метод в контролер класа. Той е отговорен да върне правилният html и с правилно заредени данни.
CallerMemberName е атрибут, който ще върне информация откъде е бил извикан този метод. Така след, като знаем името на контролера и името на html файла, ние може да открием къде се намира и го върнем на потребителя, но при това използваме нашият метод ParseTemplate, който ще помогне за зарeждане на допълнителна информация.
Redirect метод
Models папка
Models папката, ще съдържа нашите модели, които ще си комуникират с базата за в бъдеще. Сега създайте един клас Album със следните пропъртита:
Resources папка
Изтеглете от ресурсите на сайта тази папка и я добавете към вашият проект.
Views папка
Изтеглете от ресурсите на сайта тази папка и я добавете към вашият проект. В нея ще откриете вече разписаните html файлове.
Controllers папка
В Controllers папката добавете два нови класа: HomeController и AlbumsController.
HomeController класa трябва да съдържа метод: IHttpResponse Index(IHttpRequest httpRequest).
AlbumsController класa трябва да съдържа два метода: IHttpResponse Create(IHttpRequest httpRequest) и IHttpResponse CreateConfirm(IHttpRequest httpRequest).
Launcher клас
Този клас е нашата стартираща точка. Той съдържа Main метод в себе си. Създайте нова инстанция на ServerRoutingTable. След това регистрирайте пътищата до папките:
Сега ако стартирате проекта и имплементирате правилно Index метода в HomeController трябва да ви покаже днешната дата и час.

8. Управление на състоянието в уеб приложенията
Бисквитки
Малък файл с обикновен текст без изпълним код
Изпраща се от сървъра до браузъра на клиента
Съхранява се от браузъра на устройството на клиента
Съхранява малка част данни за конкретен клиент и уеб сайт
За какво се използват бисквитки?
Управление на сесиите = Вход, колички за пазаруване, резултати от игри или нещо друго, което сървърът трябва да запомни
Персонализация = Потребителски предпочитания, теми и персонализирани настройки
Проследяване = Записване и анализ на поведението на потребителя
Как се използват бисквитките?
Отговорът държи бисквитките, които трябва да бъдат запазени в Set-Cookie хедъра
Заявката съдържа специфичната за даден уебсайт бисквитка в рамките на Cookie хедъра
Структура на бисквитките
Бисквитката се състои от име, стойност и атрибути.
Атрибутите:
Двойки ключ-стойност с допълнителна информация
Не са включват в заявките
Използват се от клиента за контрол на бисквитките
Обхват = Определя се от атрибутите Domain и Path
Живот = Определя се от атрибутите Expires и Max-Age
Сигурност = Определя се от защитните флагове Secure и HttpOnly.
Разгледайте Вашите бисквитки
Местоположение на бисквитките на Mozilla
Местоположение на бисквитките на Chrome
Сесии
Сесиите представяват начин за съхраняване на информация за потребител.
Сесиите предоставят механизмът за обмен между потребителя и уеб приложението.
Структура на сесия
9. Автентикация и Авторизация
Автентикация
Процесът на проверка на самоличността на потребител или компютър
Въпроси: Кой си ти? Как го доказваш?
Поверителните данни могат да бъдат парола, смарт карта, външен маркер и т.н.
Авторизация
Процесът на определяне на това, което на потребителя е разрешено да прави на компютър или мрежа
Въпроси: Какво можете да правите? Можете ли да видите тази страница?
ASP.NET Core Identity
Системата ASP.NET Core Identity
Система за удостоверяване и упълномощаване за ASP.NET Core
Поддържа ASP.NET MVC, Pages, Web API (JWT), SignalR
Работи с потребители, потребителски профили, влизане / излизане, роли и т.н.
Работи със съгласието за бисквитки и GDPR
Поддържа външни доставчици за вход
Facebook, Google, Twitter и т.н.
Поддържа база данни, Azure, Active Directory, потребители на Windows и т.н.
Обикновено данните за идентичност на ASP.NET Core се съхраняват в релационна база данни. Данните се запазват с помощта на Entity Framework Core. Имате известен контрол върху вътрешната схема на базата данни
Настройка на идентичността на ASP.NET чрез използване на шаблоните на ASP.NET за проекти от Visual Studio.
Необходим пакет NuGet
Удостоверяване на шаблона на ASP.NET Core Project
ApplicationDbContext.cs
Съдържа контекста на данни на EF
Осигурява достъп до данните на приложението, използвайки модели на обекти
Startup.cs
Може да конфигурира удостоверяване въз основа на бисквитки (или JWT)
Може да активира външно влизане (напр. Вход във Facebook)
Може да промени настройките за идентификация по подразбиране
Може да активира RoleManager с .AddRoles ()
Настройки на паролата - могат да бъдат определени в Startup.cs
Регистрация на потребител
Потребителски Вход/Изход
Вход
Изход
ASP.NET Авторизиране
Използвайте атрибутите [Authorize] и [AllowAnonymous], за да конфигурирате разрешен / анонимен достъп за контролер / действие
Провери настоящият потребител
Добави потребител в роля
Изисква влезлия потребител в определена роля
Достъп само на потребителите в роля Administrator:
Достъп, ако Ролята на потребителя е User, Student или Trainer:
Проверете ролята на потребителя, в която сте в момента
ASP.NET Core User Manager
UserManager - API за управление на потребителите в постоянен магазин
AddClaimsAsync(…)
FindByEmailAsync(…)
GenerateChangeEmailTokenAsync(…)
AddToRoleAsync(…)
FindByIdAsync(…)
GenerateEmailConfirmationTokenAsync(…)
IsInRoleAsync(…)
FindByNameAsync(…)
GeneratePasswordResetTokenAsync(…)
GetUserId(…)
GetClaimsAsync(…)
GetAuthenticationTokenAsync(…)
ConfirmEmailAsync(…)
GetEmailAsync(…)
IsEmailConfirmedAsync(…)
ChangeEmailAsync(…)
GetRolesAsync(…)
CreateSecurityTokenAsync(…)
CreateAsync(…)
GetUserAsync(…)
ResetPasswordAsync(…)
DeleteAsync(…)
CheckPasswordAsync(…)
RemoveFromRoleAsync(…)
Dispose(…)
UpdateAsync(…)
RemoveClaimsAsync(…)
Claims
Идентичността на базата на претенции е често срещана техника, използвана в приложенията.
Искът е твърдение, което един субект прави за себе си
Идентичността на базата на претенции опростява логиката за удостоверяване
В ASP.NET Core проверките за автентичност на базата на претенции са декларативни
Изискванията за искове се основават на политиката
Претенциите са двойки име-стойност
Най-простият вид политика за искове проверява само за наличието на рекламация
Пълен контрол идентичността
Конфигуриране на LoginPath, LogoutPath, AccessDeniedPath
Видове за удостоверяване
Видове удостоверяване в ASP.NET Core приложения:
Удостоверяване и упълномощаване на базата на бисквитки (идентичност)
Удостоверяване и упълномощаване на Windows
Облачно удостоверяване и упълномощаване
JSON Уеб токени (JWT) удостоверяване и авторизация
Удостоверяване и упълномощаване въз основа на бисквитки
Базираната на бисквитки автентикация е механизмът за удостоверяване на приложението ASP.NET Core
Удостоверяването е изцяло на базата на бисквитки
Това е основна разлика от ASP.NET MVC
Главницата се основава на претенции
Удостоверяване и упълномощаване на Windows
Windows auth е по-сложен механизъм за автентификация
Разчита на операционната система за удостоверяване на потребителите
Акредитивните данни се хешират, преди да бъдат изпратени през мрежата
Най-подходящ за интранет среда
Клиенти, потребители, сървъри принадлежат към един и същ домейн на Windows (AD)
JWT удостоверяване и упълномощаване
JSON Web Tokens е модерен механизъм за авторство, базиран на JavaScript
Компактен и самостоятелен
Фокусиран върху подписани символи
Данните са криптирани
Използва се за auth & обмен на информация
Често използван при разработване на REST
Изключително проста за разбиране
Използва се в приложения Angular / React / Blazor
Социални Акаунти
Позволяването на потребителите да влизат със съществуващите си идентификационни данни е удобно
Прехвърля сложността на управлението на процеса на влизане към трета страна
Подобрява потребителското изживяване, като свежда до минимум техните авторски дейности
ASP.NET Core поддържа вградени външни доставчици за вход
Всеки доставчик на външно влизане има определен API за разработчици
Трябва да конфигурирате приложението си там, преди да го използвате
Това приложение ще ви предостави пълномощия
Тези идентификационни данни ще бъдат използвани от API на външен доставчик
Вие се удостоверявате с тях, когато изпращате заявка
Тези идентификационни данни не трябва да се съхраняват публично
Demo
1. Create ASP.NET Core web application

2. Project > Add > New Scaffolded Item...

3. Identity > Add

4. Override all files > Add

10. Сигурност на уеб приложенията
Уеб сигурността включва мерки за подобряване на сигурността на приложението.
Често се прави чрез поправяне и предотвратяване на уязвимости в сигурността.
Едно нещо се счита за сигурно, когато разходите за пробив струват повече от стойността, получена по този начин.
Нарушенията на сигурността често се случват спонтанно. Уязвимостта може да бъде напълно непреднамерена.
Нарушенията на сигурността са резултат от злонамерени атаки. Тези атаки може да имат много мотиви, които ги подкрепят. Предизвикателство, любопитство, вандализиране, кражба.
Нарушенията на сигурността могат да бъдат напълно дискретни. Силно опитни нападатели няма да оставят следа. Най-вероятно ще разберете, че сте били нападнати доста по-късно.
Основи на Сигурността
Съществува широк спектър от известни видове заплахи и атаки
Валидиране на входа
Преливане на буфер, скриптове, SQL инжекция, канонизация
Подправяне на параметри
Манипулиране на низове за заявки, манипулация на полето на формуляра, манипулиране на бисквитки, манипулация на HTTP хедъри
Управление на сесиите
Открадване на сесия, session replay, man-in-the-middle
Криптография
Лошо генериране на ключове или управление на ключове, слабо или персонализирано криптиране
Чувствителна информация
Достъп до чувствителен код или данни в хранилището, подслушване на мрежата, подправяне на код / данни, администраторска парола
Управление на изключенията
Разкриване на информация, отказ на услугата
Някои от най-добрите действия, които един програмист може да предприеме, за подсигуряване на приложението:
Максимална простота
Подсигуряване на най-слабата връзка
Ограничаване на публично достъпните ресурси
Неправилно, докато не се докаже правилно
Принципът Weakest Privilege
Сигурност при грешки
Осигуряване на постоянна защита
SQL Injection
Следните SQL команди се изпълняват:
Оригинална SQL заявка
Задаване на потребителско име на John & парола на ' OR '1'= '1
Резултатът: Потребителят с потребителско име – "Admin" ще влезе БЕЗ парола. Заявката за преминаване ще се превърне в bool израз, който винаги е верен.
Cross Site Scripting (XSS)
Cross-site scripting (XSS) е често срещана уязвимост в уеб приложенията
Уеб приложенията показват JavaScript код. Изпълнява се в браузъра на клиента. Хакерите могат да поемат контрол над сесиите, бисквитките, паролите и т.н.
Как да се предпазим от XSS? Проверете потребителския вход (вградено в ASP.NET Core). Изпълнявайте HTML escaping при показване на текстови данни.
Cross-site scripting атака:
Кражба на бисквитки
Кражба на акаунт
Промяна на съдържанието
Променете потребителските настройки
Изтеглете зловреден софтуер
Изпращане на CRSF атаки
Подсказване на парола
Cross-Site Request Forgery (CSRF)
Това е атака на уеб сигурност над HTTP протокола
Позволява изпълнението на неоторизирани команди от името на някой потребител
Потребителят има валидни разрешения за изпълнение на заявената команда
Нападателят използва тези разрешения злонамерено, без знанието на потребителя
Процесът не е толкова сложен за разбиране:
Потребителят има валидна бисквитка за автентикация до victim.org. Съхранява се в браузъра
Нападателят моли потребителя да посети http://evilsite.com. Нападателят взема съхранената бисквитка
Злият сайт изпраща HTTP Заявка до victim.org чрез бисквитката
victim.org извършва действия от името на потребителя. Действията се извършват с данните на потребителя
Какво е всъщност Cross-Site Request Forgery:
Потребителят дори може грешно да кликне бутона
Това ще активира атаката
Сигурността срещу подобни атаки е необходима
Защитава както вашето приложение, така и вашите клиенти
Parameter Tampering
Parameter Tampering е манипулиране на параметри, обменяни между клиент и сървър
Променени низове за запитвания, тяло на заявка, бисквитки
Пропуснати валидации на данните, инжектирани допълнителни параметри
Други заплахи за сигурността
Семантични URL/HTTP атаки (URL/HTTP манипулация). Винаги проверявайте данните от страна на сървъра
Man in the Middle (винаги ползвайте SSL)
Недостатъчен контрол на достъпа
Други видове инжектиране на данни (винаги санирайте данните)
DoS и DDoS и Brute Force attacks (CAPTCHA и Firewall)
Phishing и Social Engineering (образовайте потребителите си)
Пропуски в сигурността на други софтуери (използвайте последните версии
11. Създаване на REST API
JSON
JavaScript Object Notation (JSON) е файлов формат с отворен стандарт:
Използва четим от човека текст за предаване на обекти с данни
Обектите на данни се състоят от двойки атрибут-стойност или типове данни от масив
Лесно за хората да четат и пишат
Лесно за машините да обработват и генерират
JSON произлиза от JavaScript:
Независим от езика
Сега много езици предоставят код за генериране и обработване на JSON
JSON е много често използван формат на данни, използван в уеб комуникацията:
Основно в комуникация браузър-сървър или сървър-сървър
Официалният тип интернет медия (MIME) за JSON е application/json
JSON файловете имат разширение .json
JSON обикновено се използва като заместител на XML в AJAX:
По-кратък и лесен за разбиране
По-бърз за четене и писане и е по-интуитивен
Не поддържа схеми и пространства от имена
XML
XML дефинира набор от правила за кодиране на документи:
Идва от Extensible Markup Language
Подобен на JSON: По отношение на читаемостта от човека и обработката от машини, По отношение на йерархия (стойности в стойности)
XML е текстов формат:
Силна поддръжка за различни човешки езици чрез Unicode
Дизайнът се фокусира силно върху действителните документи
Има 2 типа MIME за XML application/xml и text/xml.
Има много приложения:
Широко използван в SOA
Конфигуриране на .NET приложения
Използва се във формати на Microsoft Office
XHTML е трябвало да бъде строг HTML формат
Web API
Web API е интерфейс за програмиране на приложения:
Използван от Web Browser (SPA), Mobile Applications, Games, Desktop Applications, Web Server
Състои се от публично изложени крайни точки (endpoint-и):
Крайните точки съответстват на дефинирана система за заявка-отговор
Комуникацията обикновено се изразява във формат JSON или XML
Комуникацията обикновено се осъществява чрез уеб протокол. Най-често HTTP - чрез уеб сървър, базиран на HTTP
ASP.NET Core Web API
Няма нищо различно от уеб приложение
Вие изграждате контролери с действия
В този случай обаче действията са в ролята на крайни точки
Контролерите трябва да се анотират с ApiController
ASP.NET Core Web API Controller
Наследяваме Controller
Трябва да анотираме класа с атрибутите [ApiController] и [Route]
ASP.NET Core Web API (ApiController)
Анотацията [ApiController] предоставя удобни функции:
Автоматични HTTP 400 отговор (за грешки в състоянието на модела)
Обвързване на изходния параметър на източника
Изисквания за Атрибутно рутиране
Подробни отговори за кодове за състояние на грешка
Автоматични HTTP 400 отговори
Грешките при валидиране на модела автоматично задействат HTTP 400 отговор
Обвързване на атрибути на източника
Атрибутите определят местоположението на стойността на параметъра
Multipart / Form-data заявката се подразбира
Постига се чрез поставяне на атрибута [FromForm] върху параметрите на действието
Рутирането на атрибутите се превръща в изискване
Крайните точки са недостъпни по пътищата, определени от :
UseMvc() и UseMvcWithDefaultRoute()
Отговори за подробности за проблема за кодове за състояние на грешка
От ASP.NET Core 2.2 MVC преобразува резултатите от грешки
Грешките се трансформират в ProblemDetails: Тип, базиран на HTTP Api за представяне на грешки; Стандартизиран формат за машинно четими данни за грешки
Тези функции са вградени и активни по подразбиране
Поведението по подразбиране може да бъде презаписано
ASP.NET Core Web API (Return Types)
ASP.NET Core предлага няколко опции за типове връщане на API Endpoint.
Специфичен тип = Най-простият тип действие
IActionResult тип = Подходящо, когато са възможни няколко типа ActionResult в съответното действие
Препоръчва се използването на ActionResult
ASP.NET Core Web API (GET Методи)
Създаване на уеб API с един контролер
ASP.NET Core Web API (POST Методи)
Създаване на уеб API с един контролер
Методът CreatedAtAction:
Връща 201 (Created) отговор - стандарт за POST заявки
Добавя Location хедър към отговора
Използва път с име "GetProduct", за създаване на URL
ASP.NET Core Web API (PUT Методи)
Създаване на уеб API с един контролер
Подобно на PostProduct, но използва HTTP PUT
Отговорът е 204 (No Content)
HTTP PUT изисква цяла актуализация на записа
ASP.NET Core Web API (DELETE Методи)
Създаване на уеб API с един контролер
Отговорът е 204 (No Content)
И с това ние имаме нашия Products Web API
Сега нека да тестваме крайните точки
12. Консумиране на REST API
Messages
След като в последното занятие си създадохме RestApi за съобщения, сега е време да имплементиране и Front-End частта.
Ще ни бъде предоставена обикновена HTML страница, оформена с Bootstrap. Страницата е конструирана като Front-End частта на приложението WebChat. Съдържа проста форма за избор на текущото потребителско име и проста форма за изпращане на съобщения. Също така има списък с изпратените съобщения, които са всички съобщения, които в момента са в базата данни на приложението.
Не е необходимо да пишем какъвто и да е CSS. Въпреки това ще получим файл app.js, който трябва да допишем. Уеб API-то ни връща JSON обекти и нашата задача е да ги рендерираме на страницата с JavaSctipt.
Функционалност
Username
След избора на потребителско име (натискане на бутона [Choose]) следва да се появи следният изглед.
След като кликнем върху [Reset], Потребителското име трябва да бъде нулирано и да можем да изберем друго Потребителско име.
Messaging
След натискане на бутона [Send] съобщението трябва да бъде изпратено до уеб API-а и то да бъде създадено в базата данни. Всички съобщения трябва да бъдат обновени (изброени отново), така че новото съобщение да може да бъде прикачено.
Micro-Validations
Нека въведем микро-валидации както следва:
Не трябва да можем да изпращаме съобщение, без първо да сме избрали потребителско име
Не трябва да можем да избераме празно потребителско име
Не трябва да можем да изпращаме празно съобщение
13. Deployment
Last updated