WebServerController.cs
using System;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using ExampleWebServer.Models;
/*
Раскомментировать для загрузки файла в режиме form-data в Postman
using System.Threading.Tasks;
using System.IO;
using Microsoft.AspNetCore.Http;
*/
namespace ExampleWebServer.Controllers
{
[Route(template: "/")]
[ApiController]
public class WebServerController : ControllerBase
{
private readonly ExampleWebServerContext _context;
// Конструктор класса WebServerController
public WebServerController(ExampleWebServerContext context)
{
_context = context;
Console.WriteLine("Конструктор WebServerController .......");
}
/*
https://localhost:5001/job/Int
Get запрос для таблицы Job, поиск по полю JobId
Запрос выводит поля одной записи Job и AssignedTo для EmployeID
*/
[HttpGet("job/{JobId:int}", Name = "GetJobId")]
public ActionResult<Job> GetByJobId(int JobId)
{
Console.WriteLine("Запрос для таблицы Job по полю JobId " + JobId.ToString());
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
where (j.JobId == JobId)
select new {
jobId = j.JobId,
titleJob = j.TitleJob,
dueDate = j.DueDate,
isComplete = j.IsComplete,
assignedTo = e.EmployeId
};
if (query != null) return Ok(query.First()); else return NotFound();
}
/*
https://localhost:5001/employe/Int
Get запрос для таблицы Employe по полю EmployeID
Запрос выводит одну запись Employe и связанные записи в виде массива Job
*/
[HttpGet("employe/{EmployeId:int}", Name = "GetEmployeId")]
public ActionResult<Employe> GetByEmployeId(int EmployeId)
{
Console.WriteLine("Запрос для таблицы Employe по полю EmployeID " + EmployeId.ToString());
var query = from e in _context.Employees
where (e.EmployeId == EmployeId)
select new {
e.EmployeId, e.FirstName, e.SecondName, e.LastName, e.Phone, e.Photo, e.Jobs};
if (query == null) {return NotFound();}
return Ok(query.First());
}
/*
https://localhost:5001/job/all
// Get запрос для всех записей таблицы Job
// Запрос выводит массив записей Job и EmployeId для Employe
*/
[HttpGet("job/all", Name= "GetJobAll")]
public ActionResult GetAllJob()
{
Console.WriteLine("Все записи из таблицы Job ..........");
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
select new {
jobId = j.JobId,
titleJob = j.TitleJob,
dueDate = j.DueDate,
isComplete = j.IsComplete,
assignedTo = e.EmployeId
};
if (query != null) return Ok(query); else return NotFound();
}
/*
https://localhost:5001/employe/all
Get запрос для всех записей таблицы Employe со связанными Jobs
Запрос выводит все записи таблицы Employe и связанные с ними Job
*/
[HttpGet("employe/all", Name = "GetEmployeAll")]
public ActionResult<Employe> GetAllEmploye()
{
Console.WriteLine("Все записи из таблицы Employe ..........");
var query = from e in _context.Employees
select new {
e.EmployeId, e.FirstName, e.SecondName, e.LastName, e.Phone, e.Photo, e.Jobs};
if (query == null) {return NotFound();}
return Ok(query);
}
/*
https://localhost:5001/all
Get запрос для всех записей из таблиц Employe + Job.
Запрос выводит все записи Employe и присоединеные к ним поля Job
в виде единого массива. Форма выода отличается от https://localhost:5001/employe/all
*/
[HttpGet("all")]
public ActionResult<Employe> GetAll()
{
Console.WriteLine("Все записи из таблиц Employe и Job ......");
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
select new {e.EmployeId,e.FirstName,e.SecondName,e.LastName, e.Photo, e.Phone,
j.JobId,j.TitleJob, j.DueDate, j.IsComplete};
if (query != null) return Ok(query); else return NotFound();
}
/*
https://localhost:5001/?firstname=иван&secondname=иваныч&lastname=иванов
Get запрос для таблицы Employe по параметрам: FirstName, SecondName, LastName.
Запрос выводит массив записей Employe и массив связанных с записями Job
*/
[HttpGet]
public ActionResult<Employe> Get([FromQuery(Name = "FirstName")] string FirstName,
[FromQuery(Name = "SecondName")] string SecondName,
[FromQuery(Name = "Lastname")] string LastName)
{
Console.WriteLine("Get запрос Employe по параметрам: " + "FirstName: " + FirstName +
" SecondName: " + SecondName + " LastName: " + LastName );
bool firstParam = string.IsNullOrEmpty(FirstName);
bool secondParam = string.IsNullOrEmpty(SecondName);
bool lastParam = string.IsNullOrEmpty(LastName);
// Если все три параметра не равны null or empty
if (!firstParam && !secondParam && !lastParam )
{
var query = from t in _context.Employees
where (t.FirstName.Equals(FirstName) && t.LastName.Equals(LastName) && t.SecondName.Equals(SecondName))
select new {t.EmployeId, t.FirstName, t.SecondName, t.LastName, t.Phone, t.Photo, t.Jobs};
if ((query == null) || (query.Count() == 0 )) {
Console.WriteLine("Get запрос по парметрам равен null");
return NotFound();
}
return Ok(query);
}
else
{
Console.WriteLine("Get запрос по парметрам: не все параметры определны");
return NotFound();
}
}
/*
Post запрос: https://localhost:5001/employe
Добавление новой записи в таблицу Employe.
Значение Job = null
{
"firstName": "Матвей",
"secondName": "Алексеевич",
"lastName": "Шмидт",
"phone": "921-215-45-72",
"photo": "Байтовый массив",
"jobs": null
}
*/
[HttpPost("employe/")]
public IActionResult CreateEmploye(Employe item)
{
Console.WriteLine("Employe добавление записи ........" + item.ToString());
_context.Employees.Add(item);
_context.SaveChanges();
// Выводит последнюю созданную запись в таблице Employe
return CreatedAtRoute("GetEmployeId", new { EmployeId = item.EmployeId }, item);
}
/*
https://localhost:5001/job/Int
Post запрос - добавление новой записи в таблицу Job и привязка ее к Employe
int EmployeId - к какой записи привязывать, Job item - содержимое записи Job.
Запрос выводит объединеный массив записей EmployeId + Job для выбранного EmployeId
*/
[HttpPost("job/{EmployeId:int}")]
public IActionResult CreateJob(int EmployeId, Job item )
{
Console.WriteLine("Job добавление записи ........" + item.ToString());
// Найдем запись в таблице Employe
var record = _context.Employees.Find(EmployeId);
if (record== null){return NotFound();}
// Установим связь
item.AssignedTo = record;
// Добавим и сохраним
_context.Jobs.Add(item);
_context.SaveChanges();
// Сфоромируем запрос по обоим таблицам - какие привязки есть у EmployeId
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
where (e.EmployeId == EmployeId)
select new {e.EmployeId,e.FirstName,e.SecondName,e.LastName,e.Photo,e.Phone,
j.JobId,j.TitleJob, j.DueDate};
if (query != null) return Ok(query); else return NotFound();
}
/*
PUT запрос: https://localhost:5001/employe/Int
Обновление записи в таблице Employe по номеру EmployeId
Employe item - содержимое для обновления
При запросе: https://localhost:5001/employe/1
и содержимом:
{
"firstName": "Владимир",
"secondName": "Алексеевич",
"lastName": "Petrovs",
"phone": "921-215-45-72",
"jobs": [{
"titleJob": "Job 1234",
"dueDate": "2018-09-12T14:22:54.485596",
"isComplete": true
}]
}
обновляется первая запись в таблице Employe, добавляется запись в Job
и привязывается к первой записив Employee. Если job = null записи не
добавляются.
Выводится обновленная запись и массив присоединенных Job
*/
[HttpPut("employe/{EmployeId:int}")]
public IActionResult UpdateEmploye(int EmployeId, Employe item)
{
Console.WriteLine("Обновление записи в таблице Employe ..........");
// Найдем запись
var record = _context.Employees.Find(EmployeId);
if (record == null){return NotFound();}
// Обновим переменные
record.FirstName = item.FirstName;
record.SecondName = item.SecondName;
record.LastName = item.LastName;
record.Phone = item.Phone;
record.Photo = item.Photo;
// record.Jobs = item.Jobs; // раскомментировать для массива
// Сохраним
_context.Employees.Update(record);
_context.SaveChanges();
/*
Раскомментировать для массива
// Вывод массива Employe + Job
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
where (e.EmployeId == EmployeId)
select new {e.EmployeId,e.FirstName,e.SecondName,e.LastName,e.Phone,e.Photo,
j.JobId,j.TitleJob, j.DueDate, j.IsComplete};
if (query != null) return Ok(query ); else return NotFound();
*/
// Выведем одну запись
var updateRecord = _context.Employees.Find(EmployeId);
if (updateRecord != null) return Ok(updateRecord); else return NotFound();
}
/*
PUT запрос: https://localhost:5001/job/Int
Обновление одной записи в таблице Job.
int JobId - номер обновляемой записи, Job item - обновляемые данные
Выводится одна запись Job.
При запросе вида:
https://localhost:5001/job/1 и содержимом в Postam:
{
"titleJob": "Job 10",
"dueDate": "2018-09-12T14:22:54.485596",
"isComplete": true,
"assignedTo": {
"firstName": "1",
"secondName": "Иваныч",
"lastName": "Иванов",
"phone": "921-215-45-70"
}
Будет обновлена первая запись в таблице Job и создана новая запись в таблице Employe
к которой будет привязана обновленная запись 1.
*/
[HttpPut("job/{JobId:int}")]
public IActionResult UpdateJob(int JobId, Job item)
{
Console.WriteLine("Обновление записи в таблице Job..........");
// Найдем запись
var record = _context.Jobs.Find(JobId);
if (record == null){return NotFound();}
// Обновим данные
record.TitleJob = item.TitleJob;
record.DueDate = item.DueDate;
record.IsComplete = item.IsComplete;
record.AssignedTo = item.AssignedTo;
_context.Jobs.Update(record);
_context.SaveChanges();
/*
Раскомментировать для создания новой записи Employe и вывода массива.
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
select new {e.EmployeId,e.FirstName,e.SecondName,e.LastName,e.Phone,
j.JobId,j.TitleJob, j.DueDate};
if (query != null) return Ok(query); else return NotFound();
*/
var updateRecord = _context.Jobs.Find(JobId);
if (updateRecord != null) return Ok(updateRecord); else return NotFound();
}
/*
Delete запрос: https://localhost:5001/employe/Int
Удаление Int записи в таблице Employe и обнуление связей в таблице Job
Выводится удаленная запись.
Можно вывести все оставшиеся записи - раскомментировать строку
*/
[HttpDelete("employe/{EmployeId:int}")]
public IActionResult DeleteEmploye(int EmployeId)
{
Console.WriteLine("Удаление записи EmployeId: " + EmployeId.ToString() + " в Employe ........ ");
// Найдем запись в Employe
var record= _context.Employees.Find(EmployeId);
// Если такой нет - уйдем
if (record == null){return NotFound();}
// Загрузим запись из Job
_context.Entry(record).Collection(c => c.Jobs).Load();
// Удалим Employe, связь в поле Assigned таблицы Job обнулится
_context.Employees.Remove(record);
// Сохраним
_context.SaveChanges();
return CreatedAtRoute("GetEmployeId", new { EmployeId = record.EmployeId }, record);
// var records = _context.Employees;
// if (records != null) return Ok(records); else return NotFound();
}
/*
Delete запрос: https://localhost:5001/job/5
Удаление, например, пятой записи в таблице Job
Возвращает удаленную запись.
*/
[HttpDelete("job/{JobId:int}", Name = "DeleteJobId")]
public IActionResult DeleteJob(int JobId)
{
Console.WriteLine("Удаление записи JobId: " + JobId.ToString() + " в Job ........ ");
// Найдем запись в Employe
var record= _context.Jobs.Find(JobId);
// Если такой нет - уйдем
if (record == null){return NotFound();}
// Удалим
_context.Jobs.Remove(record);
// Сохраним
_context.SaveChanges();
return Ok(record);
// var records = _context.Job;
// if (records != null) return Ok(records); else return NotFound();
}
/*
Get запрос https://localhost:5001/JobId/EmployeID
Привязка записи Job к записи в Employee.
Привязка осуществляется и при создании новой записи в Job.
Выводится объединенный массив Employe для EmployeID
*/
[HttpGet("{JobId:int}/{EmployeId:int}")]
public IActionResult AssignTo(int JobId, int EmployeID)
{
Console.WriteLine("Переназначение записи JobId: " + JobId.ToString() + " для EmployeID " + EmployeID.ToString());
var jobRecord = _context.Jobs.Find(JobId);
var exampleRecord = _context.Employees.Find(EmployeID);
jobRecord.AssignedTo = exampleRecord;
_context.SaveChanges();
var query = from j in _context.Jobs
join e in _context.Employees on j.AssignedTo.EmployeId equals e.EmployeId
where (e.EmployeId == EmployeID)
select new {e.EmployeId,e.FirstName,e.SecondName,e.LastName,e.Phone,
j.JobId,j.TitleJob, j.DueDate};
return Ok(query);
}
/* Пример простой загрузки файла на серер в режиме form-data в Postman
[HttpPost("employe/")]
public async Task<IActionResult> LoadFile(IFormFile file)
{
using (var sr = new StreamReader(file.OpenReadStream()))
{
var content = await sr.ReadToEndAsync();
var filePath = Path.GetTempFileName();
Console.WriteLine( filePath);
return Ok(content);
}
}
*/
}
}
REST API macOS. Server Application.
В статье коротко описано клиент/серверное приложение, реализующее REST API на macOS 10.14.2 (Mojave).
Серверная часть выполнена средствами .NET Core для Ubuntu и содержит SQL Server. Но почему выбран инструментарий от Microsoft? По двум причинам.
Во первых, интересно посмотреть, как он выглядит на macOS, во вторых, на LINQ любой запрос к базе данных можно реализовать в несколько строк.
Итак, подробнее. Для разработки программ на сервере применяется язык C#. LINQ и Entity Framework использованы для создания базы данных и формирования запросов к ней. В качестве контейнера использован Docker. Для разработки кода на C# установлена среда Visual Studio Code. Для формирования запросов к Web Server на этапе отладки в качестве клиента использован Postman. Для визуального управления базой данных применяется SQL Operation Studio. Статья не содержит описания установки перечисленных программных средств, это хорошо сделано здесь. Пример сервера приложения можно свободно загрузить. Для запуска проекта достаточно распаковать архив и запустить его в VS Code. Цель проекта состояла в том, чтобы оценить трудоемкость реализации REST API на клиентской части приложения под macOS. Клиент разработан в XCode на языке Swift 4.2. Описание и тексты клиента будут опубликованы во второй части статьи. 1. Структура базы данных ExampleForMac.
Структура базы данных выбрана простой и содержит пару таблиц c именами: Employees и Jobs.
Обратите внимание на поле Photo и его тип, оно содержит фотографии сотрудников. А это поля таблицы Jobs: Таблицы Employees и Jobs связаны по полям EmployeID и AssignedToEmployeId соответственно, по типу "один-ко-многим". Создание базы данных ExampleForMac, указанных выше таблиц, записей в них, а так-же установка связей между записями выполняется в файле Startup.cs 2. Формирование запросов к Web серверу Kestrel.
Вот так выглядит сервер приложения, запущенный в VS Code.
Исходный текст файла WebServerController.cs
3. HTTP запросы.
Файл WebServerController.cs содержит болee десятка HTTP запросов различных типов: Get,Put,Post,Delete.
Есть пример запроса AssignedTo, который переназначает связи в основной и подчиненной таблицах базы данных.
Каждый запрос имеет подробный комментарий в тексте файла.
4. Впечатления от инструментария.
Docker работает стабильно, замедлений и сбоев не замечено.
Комплекс программ для macOS от Microsoft - на данном этапе замечаний нет. Postman - очень полезная утилита. SQL Operation Studio - программа находится в стадии развития (версия 0.32.9), но функции свои выполняет. Всего доброго, Евгений Вересов.
10.01.2019 года.
|