Web Api interaction between multiple controller endpoints - c#

I am looking for a bit of constructive advice. I am trying to map how multiple controller endpoints can interact with each other such that I do not end up writing the same code in multiple controllers. Allow me to illustrate with a simple Kanban example.
I am thinking two(or three) controllers here: BoardController, CardController, SubCardController (undecided). For now let's ignore the fact that cards can be organised into lists, for that we would have a ListController sitting between Board and Card controllers.
Board controller:
public class BoardController : ApiController {
// among basic CRUD methods I have a method that returns single board
// implementation #1
// /api/board/getbyid?id=123
public HttpResponseMessage GetById(int id) {
var board = dataStore.GetById(id);
if (board == null)
return Request.CreateResponse(HttpStatusCode.NotFound);
return Request.CreateResponse(HttpStatusCode.OK, board);
}
// implementation #2
// /api/board/getbyid?id=123
public HttpResponseMessage GetById(int id) {
var board = GetById(id);
if (board == null)
return Request.CreateResponse(HttpStatusCode.NotFound);
return Request.CreateResponse(HttpStatusCode.OK, board);
}
[NonAction]
// normally methods like this one I declare as private
// but in this case CardController needs to call this method as well
public static Board GetById(int id) {
return dataStore.GetById(id);
}
}
Allow me to clarify that dataStore is a reference to another controller that is solely responsible for data access.
Card controller:
public class CardController : ApiController {
// among basic CRUD methods I might want to call BoardControllers GetById(id) method (to verify if board exists for example)
// implementation #1
public HttpResponseMessage GetAll(int boardId) {
// call BoardController.GetById(id) by issueing HTTP request
HttpRequestMessage _request = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage _response = Client.SendAsync(_request).Result;
Board board = _response.Content.ReadAsAsync<Board>().Result;
if (board == null /* || more condition(s) */)
return Request.CreateResponse(HttpStatusCode.NotFound);
// get cards and return
}
// implementation #2
public HttpResponseMessage GetAll(int boardId) {
// call BoardController.GetById(id) static method
var board = _boardCtrl.GetById(boardId);
if (board == null /* || more condition(s) */)
return Request.CreateResponse(HttpStatusCode.NotFound);
// get cards and return
}
}
That's my two alternative solutions. One one hand implementation #1 does not require additional static methods but on the other, I think it is a bad to issue HTTP requests from one controller action to access another controller action.
Let me know what you guys think. Particularly if there is even more neater alternative.

Create services and inject them into dependent controllers as needed. Controller should be kept lean and not focus on implementation concerns.
public interface IBoardService {
Board GetById(int id);
//...other code removed for brevity
}
public class BoardController : ApiController {
readonly IBoardService dataSource;
public BoardController(IBoardService dataSource) {
this.dataSource = dataSource;
}
// /api/board/getbyid?id=123
public IHttpActionResult GetById(int id) {
var board = dataSource.GetById(id);
return board == null ? NotFound() : OK(board);
}
}
The board service can also be reused to be injected into the CardController if it needs it
public class CardController : ApiController {
readonly IBoardService boardService;
readonly ICardService cardService;
public CardController(ICardService cardService, IBoardService boardService) {
this.cardService = cardService;
this.boardService = boardService;
}
public IHttpActionResult GetAll(int boardId) {
var board = boardService.GetById(boardId);
if (board == null /* || more condition(s) */)
return NotFound();
// get cards from card service and return
}
}
Using the injected service approach allows for it to be reused where needed.
Also, try to keep your controllers lean.
No need for all that logic in the controller.
The implementation of card service can depend on the board service and expose just a GetAllByBoardId(int id) which will reduce the logic in the CardController like in your example.

Related

Create an IEnumerable<AbstractClass> of specific instances from different classes all inheriting from the abstract class

I have controllers which, for the sake of backwards compatibility, only have one action. The JSON request comes with an attribute "type" which determines what the action should do with it.
My idea for a clean solution was to build a set of action handlers. They all inherit from an abstract class called ActionHandler which has two methods
public abstract bool CanHandle(ClientRequest request);
and
public abstract object Handle(dynamic request)
And it has a property
public abstract string ActionForController { get; }
in which the specific actionhandlers just return the name of the controller they want to handle for. This is not very important, but may help clarify something later.
The controller is inserted with an ActionHandlerRegister which has an IEnumerable and a method "GetActionHandler". It returns the first specific ActionHandler that can handle the request.
public ActionHandler GetActionHandler(ClientRequest request)
{
foreach(var actionHandler in ActionHandlers)
{
if (actionHandler.CanHandle(request))
{
return actionHandler;
}
}
throw new BadRequestException(string.Format(CultureInfo.InvariantCulture, BadRequestExceptionTemplate, request.Type));
}
The controllers look like this:
public class LogController : ControllerBase
{
private readonly IActionHandlerRegister<LogController> logHandlers;
public LogController(IActionHandlerRegister<LogController> logHandlers)
{
this.logHandlers = logHandlers ?? throw new ArgumentNullException(nameof(logHandlers));
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] dynamic rawJson)
{
var jsonBody = ((JsonElement)rawJson).ToString();
if (string.IsNullOrEmpty(jsonBody))
{
return BadRequest(ActionHandler.BadRequestRequestNullOrTypeMissingError);
}
var clientRequest = JsonSerializer.Deserialize<ClientRequest>(jsonBody);
if (clientRequest == null || string.IsNullOrEmpty(clientRequest.Type))
{
return BadRequest(ActionHandler.BadRequestRequestNullOrTypeMissingError);
}
try
{
var handler = logHandlers.GetActionHandler(clientRequest);
var result = handler.Handle(rawJson);
return Ok(result);
}
catch (BadRequestException ex)
{
return BadRequest(ex.Message);
}
}
}
For people paying attention: yes, I'm passing the rawjson to handler.Handle. This is because "ClientRequest" is something generic (from which I can read the type) but the handler needs the specific request, so it's deserializing again to something more specific. Maybe there are better solutions for that. Feel free to tell me.
In startup.cs, the insertion of the ActionHandlerRegister into the controller looks like this:
public void RegisterActionHandlersAsSingleton(IServiceCollection services)
{
IEnumerable<ActionHandler> listOfActionHandlers =
from domainAssembly in AppDomain.CurrentDomain.GetAssemblies()
from actionHandlerType in domainAssembly.GetTypes()
where actionHandlerType.IsAssignableFrom(typeof(ActionHandler))
select (ActionHandler)Activator.CreateInstance(actionHandlerType);
services.AddSingleton<IActionHandlerRegister<LogController>>(new ActionHandlerRegister<LogController>(listOfActionHandlers.Where(a => a.ActionForController == nameof(LogController))));
// other controllers ...
}
You might be able to guess, this last piece of code crashes at runtime telling me it's unable to cast to ActionHandler.
System.InvalidCastException: Unable to cast object of type
'System.Object' to type
'TcServerModules.ActionHandlers.ActionHandler'.
I have been playing around with different solutions, but none of them scratch that itch. What would be a nice, true-to OO-design principle

Unit testing the code that is written using repository pattern

I am implemented my business logic using repository pattern. I basically have Approve method in my controller . I am calling the service method ApproveUserChangeRequest
which in turn invokes GetUserChangeRequest and ApproveUserChangeRequest in the UnitofWork class. I would like to know if this is standard or better way of doing it
Please bare in mind in to test the service methods
UserConroller
[HttpPost]
[AllowAnonymous]
[Route("approve-change-request")]
public IActionResult ApproveUserChangeRequest([FromBody] ApproveUserChangeRequests approveUserChangeRequests)
{
if (!ModelState.IsValid)
{
return BadRequest(new ResponseModel()
{
ResponseMessages = new Dictionary<string, string[]>
{
{ "Errors", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToArray() }
}
});
}
var result = _userService.ApproveUserChangeRequest(approveUserChangeRequests);
var message = string.Empty;
if (result.Succeeded)
{
return Ok(new ResponseModel()
{
ResponseMessages = new Dictionary<string, string[]>
{
{ "Info", new string[] { $"True" } }
}
});
}
message = string.Join(";", result.Errors.Select(x => $"Code: {x.Code}. Description: {x.Description}"));
_logger.Error(new IdentityException($"Error approving user change requests. Message: {message}"));
return BadRequest();
}
UserService class
public IdentityResult ApproveUserChangeRequest(ApproveUserChangeRequests approveUserChangeRequests)
{
var userChangeRequest = _userUow.GetUserChangeRequest(approveUserChangeRequests.UserChangeRequestID);
IdentityResult result = _userUow.ApproveUserChangeRequest(userChangeRequest, approveUserChangeRequests.ApprovedByAuthUserId, approveUserChangeRequests.AuthApplicationName);
return result;
}
UnitofWork class (uow)
public UserChangeRequest GetUserChangeRequest(int userChangeRequestId)
{
return UserChangeRequestRepository.GetQueryable(x =>
x.Id == userChangeRequestId)
.FirstOrDefault();
}
public IdentityResult ApproveUserChangeRequest(UserChangeRequest userChangeRequest, int approvedByAuthUserId, string authApplicationName)
{
var idResult = IdentityResult.Success;
// Check if UserChangeRequest is still Pending
bool isUserChangeRequestPending = UserChangeRequestRepository.GetQueryable(x => x.Id == userChangeRequest.Id && x.ChangeStatus == "Pending").Any();
if (isUserChangeRequestPending && approvedByAuthUserId > 0)
{
// Inserting record in the UserChangeRequestApproval table
InsertUserChangeRequestApproval(userChangeRequest);
SaveContext();
//Updating the user details in IdentityDB, ClientCompanyContact and AuthUser tables
UpdateUserDetails(userChangeRequest, authApplicationName);
}
else
{
idResult = IdentityResult.Failed(new IdentityError { Description = "No userchange request to approve" });
}
return idResult;
}
It's important to only test each part of your application in isolation. When we test public IActionResult ApproveUserChangeRequest we only want to ensure that it's doing its own job correctly. Anything that it calls should be mocked, and tested separately.
For this you will need to create interfaces for your repository, and your UnitOfWork class. This will allow them to be mocked, and their behaviour simulated.
You should also allow these classes to be injected into the consuming class using Dependency Injection, for example:
private readonly IUserService _userService;
public MyController(IUserService userService)
{
_userService = userService;
}
public IActionResult ApproveUserChangeRequest([FromBody] ApproveUserChangeRequests approveUserChangeRequests)
{
// ... snip
// this now uses the instance that was provided by dependency injection
var result = _userService.ApproveUserChangeRequest(approveUserChangeRequests);
}
You would then be able to test your class/method, whilst mocking the behaviour of your user service. The following example uses Moq, but you could use another mocking framework.
public void ApproveUserChangeRequest_PassesApproveChangeRequestsModelToService()
{
// mock the user service
var userService = new Mock<IUserService>();
// provide the controller with the user service
var controller = new MyController(userService);
// create the model for the request
var model = new ApproveUserChangeRequests();
// test the method
controller.ApproveUserChangeRequest(model);
// make sure that userService.ApproveUserChangeRequest was called with the correct arguments
userService.Verify(u => u.ApproveUserChangeRequest(model));
}
In the unit tests for the Controller, you only need to check that ApproveUserChangeRequest is doing it's job correctly. That is
verifying your model
raising an error if the model isn't valid
calling the user service if it is valid
sending you the correct response if it's successful
logging correctly to your logger
responding with bad request if appropriate
These should all be checked for in separate unit tests.
You should also then write tests for your UserService, and your UnitOfWork class. Only write tests for things that those classes are responsible for. If you find that the class is responsible for too many things, refactor your class until it obeys the single responsibility principle, this will greatly aid your ability to test your code.

How to Call Api Controller from another Api Controller In Same Project

i have an object "ApplicantDetail" with list of objects in ApplicantController and i want to send Post Request to Personaldetails Api On To save into database and get response back
Api On ApplicantController
// POST: api/ApplicantDetail
[HttpPost]
[Route("~/api/ApplicantDetail")]
public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
Personaldetail personaldetail = new Personaldetail
{
Name = ApplicantDetail.Name,
Cnic = ApplicantDetail.Cnic,
Phone = ApplicantDetail.Phone
};
List<Address> addresses = new List<Address>();
List<Employee> employees = new List<Employee>();
List<Bank> banks = new List<Bank>();
addresses.AddRange(ApplicantDetail.Address);
employees.AddRange(ApplicantDetail.Employee);
banks.AddRange(ApplicantDetail.Bank);
var response = *//How to Call Personaldetail Post APi of PersonaldetailController Controller From Here and Get
response back//*
return null;
}
Api On Personaldetail Controller
// POST: api/Personaldetails
[HttpPost]
public async Task<IActionResult> PostPersonaldetail([FromBody] Personaldetail personaldetail)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Personaldetail.Add(personaldetail);
await _context.SaveChangesAsync();
return CreatedAtAction("GetPersonaldetail", new { id = personaldetail.ApplicantId }, personaldetail);
}
You should create a service class to represent the personal details API. In this class, you would inject HttpClient, and then set up IHttpClientFactory with a strongly-typed client:
public class PersonalDetailsService
{
private readonly HttpClient _client;
public PersonalDetailsService(HttpClient client)
{
_client = client;
}
// methods here to interact with API via `_client`
}
Then, in ConfigureServices:
services.AddHttpClient<PersonalDetailsService>(c =>
{
c.BaseAddress = new Uri(Configuration["PersonalDetailsAPIUrl"]);
// configure anything else you need to on the `HttpClient`
});
Finally, in your controller, you inject PersonalDetailsService:
[ApiController]
[Route("api/ApplicantDetail")]
public class ApplicantController
{
private readonly PersonalDetailsService _personalDetailsService;
public ApplicantController(PersonalDetailsService personalDetailsService)
{
_personalDetailsService = personalDetailsService;
}
[HttpPost("")]
public async Task<IActionResult> Post([FromBody] Personaldetail ApplicantDetail)
{
...
var response = await _personalDetailsService.SomeMethodAsync(someParam);
return null;
}
}
If this is within the same process it's unwise to call a controller from another controller.
It's more common to create a class, usually called a service, in which you put your logic to apply the task at hand.
This will have some benifits:
isolation of the logic
not having to worry about it when changing one endpoint
not worry about changing url's or authentication
not having an unnecessary network action
E.g.:
The service:
public class YourService
{
public void YourMethod(parameters)
{
//do your stuff
}
}
The usage:
public class Controller1 : Controller
{
public void YourAction1()
{
//controller specific stuff like model validation
//shared logic
var service = new YourService();
service.YourMethod();
}
}
public class Controller2 : Controller
{
public void YourAction2()
{
//controller specific stuff like model validation
//shared logic
var service = new YourService();
service.YourMethod();
}
}
Alternativly you can use a DI framework to resolve your service.
I could see that your target method (PostPersonaldetail) on controller2 (Personaldetail Controller) is an asynchronous method. While we are calling it we need to use the await keyword with async applied to the method (POST: api/ApplicantDetail) as we can't use await keyword in a method with out making the method as async. Alternatively we can apply wait on the method call too.
Controllers are nothing but classes, we can create an instance and call methods defined in it, however it's not a good practice. Using dependency injection we can get references to other services/controllers with in the application and use it to call methods defined on them.
Approach 1:
public async IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
var response = await new controller2().PostPersonaldetail(persondetails);
}
Approach 2:
public IActionResult Post([FromBody] Personaldetail ApplicantDetail)
{
var response = new controller2().PostPersonaldetail(persondetails).Wait();
}
The following links can be helpful.
Resource 1
Resource 2
I don't think you want to new up an instance because it could be missing dependencies. You can do something like:
var controller = DependencyResolver.Current.GetService<PostPersonaldetail>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
Then make the call.
You could also use dependency injection and just have a reference in the controller:
class ApplicantController : ControllerBase
{
public ApplicantController(ControllerB controllerb)
{
_contollerB = controllerB;
}
you may have to add a call to add the transient service:
services.AddTransient<ControllerB>();

When to return IHttpActionResult vs Object

In examples of using the ASP.NET Web API I see two different methods used to return data to the calling jQuery function. The first method returns an object of type Client but I am not sure what the second method is returning.
Method #1 (returns Client object)
public IEnumerable<Client> GetAllClients()
{
using (var context = new PQRSModel.PQRSEntities())
{
context.Configuration.ProxyCreationEnabled = false;
var query = context.Clients.OrderBy(c = c.OrgName);
var customers = query.ToList();
return customers;
}
}
Method #2 (What benefit does IHttpActionResult provide?)
public IHttpActionResult GetClient(int clientId)
{
using (var context = new PQRSModel.PQRSEntities())
{
context.Configuration.ProxyCreationEnabled = false;
var client = context.Clients.FirstOrDefault(c = c.ID == clientId);
if (client == null)
{
return NotFound();
}
return Ok(client);
}
}
If the second method finds a single object is there any reason it could not also return a Client object type?
Returning IHttpActionResult provides a nice separation of concerns.
Your controller can focus on responding to the request in the most sensible manner (status codes, error messages, etc.). Another (service) layer can focus on actually retrieving and transforming the business data.
The side-effect is, your controller methods become more unit testable. Consider the following simple example:
public class MyController : ApiController
{
//or better yet, dependency-inject this
SomeService _service = new SomeService();
public IHttpActionResult Get(int id)
{
if (id < 0)
return BadRequest("Some error message");
var data = _service.GetData(id);
if (data == null)
return NotFound();
return Ok(data);
}
}
Not only is this method's logic understandable just by reading it, but you could now test the logic more easily and naturally, something like (using NUnit syntax):
[TestFixture]
public class MyControllerTests
{
[Test]
public void Get_WithIdLessThan0_ReturnsBadRequest()
{
var controller = new MyController();
int id = -1;
IHttpActionResult actionResult = controller.Get(id);
Assert.IsInstanceOf<BadRequestErrorMessageResult>(actionResult);
}
}
Similarly, you could mock the Service layer and test what happens when you give known id parameters to the controller, etc.
Here is a good article on Unit Testing Controllers in Web Api
The second method allows you to return just status codes (like the 404 in the example), streaming file content and other types of non-object content.

call another api controller

When I call user controller (api/user), I am able to pass user credentials, however the application crashes with null exception error (Value cannot be null) in the values controller:
public class ValuesController : ApiController
{
private cdwEntities db = new cdwEntities();
public HttpResponseMessage Get([FromUri] Query query)
{
var data = db.database.AsQueryable();
if (query.name != null)
{
data = data.Where(c => c.Name == query.name);
}
if (query.price != null)
{
data = data.Where(c => c.Price == query.price);
}
if (!data.Any())
{
var message = string.Format("error");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
***return Request.CreateResponse(HttpStatusCode.OK, data);***
}
}
I believe this error is because valuescontroller method cannot pass null values as it always pass parameters(i.e. api/values/name=tom), hence when I call user controller, it throws null error because the system has not passed any parameters into the Valuescontroller from user controller.
public class UserController : ApiController
{
[Authorize]
public HttpResponseMessage Get([FromUri] Query query)
{
if (User.IsInRole("user"))
{
var result = new itemController();
return result.Get(query);
}
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
}
Is their some built-in function i could use to solve this issue or any framework/library i should look into?
Many thanks for your help and time.
Others have pointed out that you should not often (ever?) call one view controller endpoint from another, but in the case that you need/want to, you need to be sure the target has been initialized properly. This is done using ControllerBuilder.
So instead of:
var result = new itemController();
return result.Get(query); // this will blow up!
You would do:
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
itemController c = (itemController) factory.CreateController(
ControllerContext.RequestContext, "item");
return c.Get(query);
This will ensure that the target view controller has been initialized with all necessary context.
You should not call one API endpoint method from other API endpoint. Instead you need to have proper segregation of code between API, Business Logic Layer and Data Access Layer. I would do it in following way -
API -
public class UserController : ApiController
{
[Authorize]
public HttpResponseMessage Get([FromUri] Query query)
{
BusinessLayer layer = new BusinessLayer();
if (User.IsInRole("user"))
{
var result = layer.GetData(query);
// use result here and return HttpResponseMessage
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
}
}
And in your business logic layer -
public ResultModel Get(Query query)
{
// Process your model Query here... and then return some meaningful result here...
// Also call Data Access Layer Methods from here, instead of making direct database
// (entity framework) calls...
}
For better flexible and loosely coupled systems, you need to have Dependency Injection (probably using Unity, but there are many other options like Autofac, Ninject etc.)

Categories

Resources