Outline
Solution: Building Out the API Endpoints
In the Conduit.Api project, create the following Controllers.
In the body of the each controller add the following code (we'll fill in the details later):
try
{
return Ok();
}
catch (Exception ex)
{
Logger.LogError(ex.Message, ex);
return StatusCode(500, ex.Message);
}
Add the ArticlesController with the following API Structure (scroll to the right to the full table):
API | Description | Request Body | Query Parameters | Response Body
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GET /api/articles | List Articles | | Filter by tag: ?tag=AngularJS | An array of articles
Method Name: GetArticles | Returns most recent articles | | Filter by author: ?author=jake | along with the count of
| Authentication optional | | Favorited by user: ?favorited=jake | articles
| Global by default | | Limit number of articles (default is 20): ?limit=20 |
| Will return multiple articles | | Offset/skip number of articles (default is 0): ?offset=0 |
| Ordered by most recent first | | |
| Provide tag, author or | | |
| favorited query parameter | | |
| to filter results | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GET /api/articles/feed | Feed Articles | | Limit number of articles (default is 20): ?limit=20 | An array of articles
Method Name: GetArticleFeed | Will return multiple articles | | Offset/skip number of articles (default is 0): ?offset=0 | along with the count of
| Authentication required | | | articles
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GET /api/articles/:slug | Get Article | | | A single Article
Method Name: GetArticleBySlug | No Authentication required | | |
| Will return single article | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
POST /api/articles | Create Article | { | |
Method Name: CreateArticle | Authentication required | "article": { | |
| Will return single article | "title": "How to train your dragon", | |
| Required fields: | "description": "Ever wonder how?", | |
| - title | "body": "You have to believe", | |
| - description | "tagList": ["reactjs", "angularjs", "dragons"] | |
| - body | } | |
| | } | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PUT /api/articles/:slug | Update Article | { | | Returns the
Method Name: UpdateArticle | Authentication required | "article": { | | updated Article
| The slug also gets updated | "title": "How to train your dragon", | |
| when the title is changed | } | |
| Required fields: | } | |
| - title | | |
| - description | | |
| - body | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DELETE /api/articles/:slug | Delete Article | | |
Method Name: DeleteArticle | Authentication required | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
POST /api/articles/:slug/comments | Add Comments to an Article | { | | Returns the
Method Name: AddCommentToArticle | Authentication required | "comment": { | | created Comment
| Required Field: body | "body": "His name was my name too.", | |
| | } | |
| | } | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GET /api/articles/:slug/comments | Get Comments from an Article | | | returns the
Method Name: GetCommentsFromArticle | Authentication optional | | | created Comment
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DELETE /api/articles/:slug/comments/:id | Delete Comment | | |
Method Name: DeleteCommentsFromArticle | Authentication required | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
POST /api/articles/:slug/favorite | Favorite Article | | | returns the Article
Method Name: FavoriteArticle | Authentication required | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DELETE /api/articles/:slug/favorite | Unfavorite Article | | | returns the Article
Method Name: UnfavoriteArticle | Authentication required | | |
Here's the code for the ArticlesController.cs file:
using Conduit.Models.Requests;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
namespace Conduit.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ArticlesController : ControllerBase
{
private ILogger<ArticlesController> Logger;
[HttpGet]
public IActionResult GetArticlesAsync(
[FromQuery] string tag,
[FromQuery] string author,
[FromQuery] string favorited,
[FromQuery] int limit = 20,
[FromQuery] int offset = 0
)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpGet("feed")]
public IActionResult GetArticleFeedAsync([FromQuery] int limit = 20, int offset = 0)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpGet("{slug}")]
public IActionResult GetArticleBySlugAsync([FromRoute] string slug)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpPost]
[Authorize]
public IActionResult CreateArticleAsync([FromBody] ArticleRequest<ArticleCreateRequest> req)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpPut("{slug}")]
[Authorize]
public IActionResult UpdateArticleAsync(
[FromRoute] string slug,
[FromBody] ArticleRequest<ArticleUpdateRequest> req
)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpDelete("{slug}")]
[Authorize]
public IActionResult DeleteArticleAsync([FromRoute] string slug)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpPost("{slug}/comments")]
[Authorize]
public IActionResult AddCommentToArticleAsync(
[FromRoute] string slug,
[FromBody] CommentRequest<CommentAddRequest> req)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpGet("{slug}/comments")]
public IActionResult GetCommentsFromArticleAsync([FromRoute] string slug)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpDelete("{slug}/comments/{id:int}")]
[Authorize]
public IActionResult DeleteCommentsFromArticleAsync(
[FromRoute] string slug,
[FromRoute] int id)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpPost("{slug}/favorite")]
[Authorize]
public IActionResult favoriteArticleAsync([FromRoute] string slug)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
[HttpDelete("{slug}/favorite")]
[Authorize]
public IActionResult UnfavoriteArticleAsync([FromRoute] string slug)
{
try
{
return Ok();
}
catch (Exception e)
{
Logger.LogError(e.Message, e);
return StatusCode(500, e);
}
}
public ArticlesController(ILogger<ArticlesController> logger)
{
Logger = logger;
}
}
}