Dotnet api linux: Difference between revisions
Line 184: | Line 184: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
= Filtering And Searching = | =Filtering And Searching = | ||
== Filtering == | ==Filtering == | ||
Basically add an argument to the get method and implement it on the data repository checking for an argument and returning appropriately. Note the default [FromQuery] argument is the key to this being passed. E.g. mainCategory=Rum on the URI | Basically add an argument to the get method and implement it on the data repository checking for an argument and returning appropriately. Note the default [FromQuery] argument is the key to this being passed. E.g. mainCategory=Rum on the URI | ||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 204: | Line 204: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Searching== | ==Searching== | ||
To implement this just add a parameter for the searching as you did for filtering and implement appropriately | To implement this just add a parameter for the searching as you did for filtering and implement appropriately | ||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 228: | Line 228: | ||
return collection.ToList(); | return collection.ToList(); | ||
} | } | ||
</syntaxhighlight> | |||
==Combining Parameters== | |||
Make a class to contain these and pass them to the repository. | |||
<syntaxhighlight lang="c#"> | |||
public class AuthorsResourceParameters | |||
{ | |||
public string MainCategory { get; set; } | |||
public string SearchQuery { get; set; } | |||
} | |||
</syntaxhighlight> | |||
You will need to tell the controller where to find them as it is not the default. i.e. | |||
<syntaxhighlight lang="c#"> | |||
[HttpGet()] | |||
[HttpHead] | |||
public ActionResult<IEnumerable<AuthorDto>> GetAuthors([FromQuery] AuthorsResourceParameters authorsResourceParameters) | |||
{ | |||
var authorsFromRepo = _courseLibraryRepository.GetAuthors(authorsResourceParameters); | |||
return Ok(_mapper.Map<IEnumerable<AuthorDto>>(authorsFromRepo)); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 05:10, 29 July 2020
Set up with VS Code
Create Project
To create a webapi using .net core 3.1 simply type
dotnet new webapi
Restore Nuget Packages
dotnet add package Microsoft.EntityFrameworkCore
Install Migration Tool
Exercise called for using Add-Migration on windows. In Linux this translates to
# Install Tool
dotnet add package Microsoft.EntityFrameworkCore.Tools.Dotnet
# Install dotnet-ef
dotnet tool install --global dotnet-ef
# Run Migration Creation
dotnet ef migrations add InitialCreate
Install SQL Server
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
# Add to /etc/apt/source.list
# deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/ubuntu/18.04/mssql-server-2019 bionic main
sudo apt-get update
sudo apt-get install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup
# Show working
systemctl status mssql-server --no-pager
Create user
CREATE DATABASE test;
GO
CREATE LOGIN test with PASSWORD = 'guess!';
GO
EXEC master..sp_addsrvrolemember @loginame = N'test', @rolename = N'dbcreator'
GO
Running Query
List tables in DB
select schema_name(t.schema_id) as schema_name,
t.name as table_name,
t.create_date,
t.modify_date
from sys.tables t
order by schema_name,
table_name;
sqlcmd -S localhost -U test -d CourseLibraryDB -Q list_tables.sql
Structuring and Implementing
Interacting with Resources through HTTP Methods
Below is a table which shows the methods and how they work with the Authors and Courses demo app along with suggested naming. Note the use of nouns
Content Negotiation
We also need to ensure that we configure what type of media we support. In ASP .net this can be done by setting the setupAction. This will stop the default of json being returned.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(setupAction => {
setupAction.ReturnHttpNotAcceptable = true;
}).AddXmlDataContractSerializerFormatters();
...
Getting Resources
Intro
When we get resources we generally expect business objects rather than entities. These are known as DTOs (Data Transfer Objects). For C# there are other options but automapper product seems to be the product of choice for ASP .NET.
Adding Automapper
To add automapper
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
Configure Automapper
And configure the service in the code
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
...
Create a DTO Class
public class AuthorDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string MainCategory { get; set; }
}
Create a Profile Class
This class performs the mapping from the Entity to the DTO
public class AuthorsProfile : Profile
{
public AuthorsProfile()
{
CreateMap<Entities.Author, Models.AuthorDto>()
.ForMember(
dest => dest.Name,
opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"))
.ForMember(
dest => dest.Age,
opt => opt.MapFrom(src => src.DateOfBirth.GetCurrentAge()));
}
}
}
Implement the Get of collection and instance on the controller
...
[HttpGet()]
public ActionResult<IEnumerable<AuthorDto>> GetAuthors()
{
var authorsFromRepo = _courseLibraryRepository.GetAuthors();
return Ok(_mapper.Map<IEnumerable<AuthorDto>>(authorsFromRepo));
}
[HttpGet("{authorId}")]
public IActionResult GetAuthor(Guid authorId)
{
var authorsFromRepo = _courseLibraryRepository.GetAuthor(authorId);
if (authorsFromRepo == null)
{
return NotFound();
}
return Ok(_mapper.Map<AuthorDto>(authorsFromRepo));
}
Overriding Exception Errors
To control the error the consumer sees in the case of an exception, configure an exception handler in Startup.cs
...
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(appBuilder => {
appBuilder.Run(async context => {
context.Response.StatusCode = 500;
await context.Response.WriteAsync("An unexpected fault occurred. Please try again");
});
});
}
Supporting HEAD
To support just sending the result without the body simply add HttpHead to the getters.
[HttpGet()]
[HttpHead]
public ActionResult<IEnumerable<AuthorDto>> GetAuthors()
{
var authorsFromRepo = _courseLibraryRepository.GetAuthors();
return Ok(_mapper.Map<IEnumerable<AuthorDto>>(authorsFromRepo));
}
Filtering And Searching
Filtering
Basically add an argument to the get method and implement it on the data repository checking for an argument and returning appropriately. Note the default [FromQuery] argument is the key to this being passed. E.g. mainCategory=Rum on the URI
# In the controller
[HttpGet()]
[HttpHead]
public ActionResult<IEnumerable<AuthorDto>> GetAuthors(string mainCategory)
{
var authorsFromRepo = _courseLibraryRepository.GetAuthors(mainCategory);
return Ok(_mapper.Map<IEnumerable<AuthorDto>>(authorsFromRepo));
}
# In the Repository
public IEnumerable<Author> GetAuthors(string mainCategory)
{
if (string.IsNullOrEmpty(mainCategory)) return _context.Authors.ToList<Author>();
return _context.Authors.Where(x => x.MainCategory == mainCategory.Trim()).ToList();
}
Searching
To implement this just add a parameter for the searching as you did for filtering and implement appropriately
public IEnumerable<Author> GetAuthors(string mainCategory, string searchQuery)
{
if (string.IsNullOrEmpty(mainCategory) && string.IsNullOrEmpty(searchQuery)) return _context.Authors.ToList<Author>();
var collection = _context.Authors as IQueryable<Author>;
if (!string.IsNullOrEmpty(mainCategory))
{
collection = collection.Where(x => x.MainCategory == mainCategory.Trim());
}
if (!string.IsNullOrEmpty(searchQuery))
{
collection = collection.Where(
x => x.MainCategory.Contains(searchQuery.Trim()) ||
x.FirstName.Contains(searchQuery.Trim()) ||
x.LastName.Contains(searchQuery.Trim()));
}
return collection.ToList();
}
Combining Parameters
Make a class to contain these and pass them to the repository.
public class AuthorsResourceParameters
{
public string MainCategory { get; set; }
public string SearchQuery { get; set; }
}
You will need to tell the controller where to find them as it is not the default. i.e.
[HttpGet()]
[HttpHead]
public ActionResult<IEnumerable<AuthorDto>> GetAuthors([FromQuery] AuthorsResourceParameters authorsResourceParameters)
{
var authorsFromRepo = _courseLibraryRepository.GetAuthors(authorsResourceParameters);
return Ok(_mapper.Map<IEnumerable<AuthorDto>>(authorsFromRepo));
}