This project uses Wissance.WebApiToolkit
so please give us a star! And for this project too!
This project is a tutorial about how to design REST API
, we are have here 2 simultaneously existing Web projects:
REST API
withEntityFramework
ORM
-Wissance.WeatherControl.WebApi
projectREST API
withEdgeDb
Graph DB
-Wissance.WeatherControl.WebApi.V2
project
These 2 Project Have different data Model
This project target platform is a netcore 3.1
Station
- weather station that has name, description, coordinates and it can measure one or multiple weather parameters;Measurement
- one weather sample measured by station itself.
Web API REST service (.Net Core) that could store weather data from multiple weather station. We assume that we are going to store/manage following physical value measurements getting from appropriate sensors:
temperature
;atmosphere pressure
;humidity
;wind speed
;
Application has 2 resources
= Domain objects
Application uses MsSql as Database Server (this could be easily changed, but this required to re-generate migration).
Station
Measurement
This is a very simple application (demo), if any feature is needed open new issue.
- Application client create one or multiple station using Station (
/api/station
) resource (CRUD) - Client interacts with station, gets it measured data and store it using Measurements (
/api/measurements
) resource (CRUD)
It should be noted that Postman Requests stored in docs folder
- Create Station:
POST http://localhost:8058/api/station
{
"id": 0,
"name": "Yekaterinburg main station",
"description": "Yekaterinburg meteo station (meteo mountain)",
"longitude": "60°37'55\"E",
"latitude": "56°49'36\"N"
}
We got a Operation result response:
{
"success": true,
"message": null,
"data": {
"id": 1,
"name": "Yekaterinburg main station",
"description": "Yekaterinburg meteo station (meteo mountain)",
"longitude": "60°37'55\"E",
"latitude": null
}
}
- Station data update (could be updated name, description and coordinates):
PUT http://localhost:8058/api/station/1
Body and response are the same as at Create operation:
{
"id": 0,
"name": "Yekaterinburg main station",
"description": "Yekaterinburg meteo station (meteo mountain)",
"longitude": "60°37'55\"E",
"latitude": "56°49'36\"N"
}
- There are two get endpoints:
-
3.1 to get
one by id
-GET http://localhost:8058/api/station/1
-
3.2 to get
collection with paging
-GET http://localhost:8058/api/station/?page=1&size=10
- To delete station with id 1 use endpoint
DELETE http://localhost:8058/api/station/1
- Create measurements
POST http://localhost:8058/api/measurements
{
"id": 0,
"timestamp": "2022-05-24T10:13:43",
"temperature": 16.1,
"pressure": 742.3,
"humidity": 60.5,
"windSpeed": 0.5,
"stationId": 1
}
We got following result in the output:
{
"success": true,
"message": null,
"data": {
"id": 1,
"timestamp": "2022-05-24T10:13:43",
"temperature": 16.1,
"pressure": 742.3,
"humidity": 60.5,
"windSpeed": 0.5,
"stationId": 1
}
}
- Update measurements: one or any number of weather parameters could be changed using
PUT http://localhost:8058/api/measurements/1
with same body and result as at create measurements operation.
- There are two get operations:
- 3.1 to get one by id
GET http://localhost:8058/api/measurements/1
- 3.2 to get collection with paging
GET http://localhost:8058/api/measurements/?page=1&size=10
- To delete measurements with id 1 use endpoint
DELETE http://localhost:8058/api/measurements/1
Here we've got a net6.0
REST
Service that have a slightly different data model:
MeasureUnit
Measurement
Sensor
Station
Data project is Wissance.WeatherControl.GraphData
- Start
Edgedb
instance fromWissance.WeatherControl.GraphData
directory
edgedb instance start -I Wissance_WeatherControl_Data --foreground
apply migration via
edgedb migrate
- Configure Edgedb to allow pass own identifiers (necessary for object return after create)
edgedb configure set allow_user_specified_id true
- Start edgedb ui:
edgedb ui
Once you loaded project you could use it in current application:
- Add proper connection string in the database section in
appsettings.Development.json
config file:
"Application": {
"Database": {
"ConnStr": "edgedb://edgedb:VcJjK6blkKAV2MUTdJXzLPvS@localhost:10702/edgedb"
}
}
configuration string must have the following scheme: edgedb://user:password@host:port/database
you could see your project credential on Windows
machine in a directory:
%USER_PROFILE%\AppData\Local\EdgeDB\config\credentials
We are having following Key Items:
Controllers
- we are using base classes from aWissance.WebApiToollit
, in this lib we have eithther controllers for read-only and for fullCRUD
resources.Managers
- classes that are responsible for manage all business logic, in this project we have only one manager class -EdgeDbManager
that is common forCRUD
operation over all resourcesEqlResolver
- class that is responsible for associationmodel
(resource
) with operation (read
,create
,update
ordelete
)Factories
- static classes that constructsDTO
fromModels
and params (dictionary forcreate
andupdate
perform) fromDTO
.
All controllers are located in a folder Controllers
, just look how simply look full CRUD
Controller:
namespace Wissance.WeatherControl.WebApi.V2.Controllers
{
public class MeasurementController : BasicCrudController<MeasurementDto, MeasurementEntity, Guid>
{
public MeasurementController(EdgeDBClient edgeDbClient)
{
Manager = new EdgeDbManager<MeasurementDto, MeasurementEntity, Guid>(ModelType.Measurement, edgeDbClient,
MeasurementFactory.Create, MeasurementFactory.Create);
}
}
}
We have only one manager for all controllers due to the power of C# generics we just have to pass
to EdgeDbManager
:
modelType
that is using to find approptiate eql statements fromEqlResolver
EdgeDbClient
client to edgedb database- and 2 delegates that describes how to create representation (
DTO
) from model and how to convertDTO
to parameters list forinsert
andupdate
operations
Just a set of dictionaries every dictionary for one operation:
- get collection
- get one
- create
- update
- delete
They are static classes in a Factories
dicrectory, the looking quite simple:
namespace Wissance.WeatherControl.WebApi.V2.Factories
{
public static class SensorFactory
{
public static SensorDto Create(SensorEntity entity)
{
SensorDto dto = new SensorDto()
{
Id = entity.Id,
Name = entity.Name,
Latitude = entity.Latitude,
Longitude = entity.Longitude
};
if (entity.Measurements.Any())
{
dto.Measurements = entity.Measurements.Select(m => MeasurementFactory.Create((m))).ToList();
}
return dto;
}
public static IDictionary<string, object?> Create(SensorDto dto, bool generateId)
{
IDictionary<string, object?> dict = new Dictionary<string, object?>()
{
{"Name", dto.Name},
{"Latitude", dto.Latitude},
{"Longitude", dto.Longitude},
{"Measurements", dto.Measurements.Where(m => m.Id.HasValue)
.Select(m => m.Id.Value).ToArray()}
};
// TODO(this if for further getting created object)
dict["id"] = generateId ? Guid.NewGuid() : dto.Id;
return dict;
}
}
}