When to return IHttpActionResult vs Object - c#

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.

Related

Unit Testing a Controller method returns null

I'm trying to learn Unit testing in .NET 6 by testing a controller function GetProduct. The problem is I get null returned in the variable var product = await _productController.GetProduct(productId);. As you can see in the picture below, the Result is ok but the Value, where the ServiceResponse<Product> was suppose to be is null.
Here is the controller function:
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{productId}")]
public async Task<ActionResult<ServiceResponse<Product>>> GetProduct(int productId)
{
var result = await _productService.GetProductAsync(productId);
return Ok(result);
}
}
Here is the Test:
public class ProductControllerTest
{
private readonly ProductController _productController;
private readonly Mock<IProductService> _productService = new Mock<IProductService>();
public ProductControllerTest()
{
_productController = new ProductController(_productService.Object);
}
[Test]
public async Task GetProducts_ReturnsProduct_IfProductExists()
{
//Arange
var productId = 1;
var prodData = new Product
{
Id = productId,
Title = "null"
};
var prductResponse = new ServiceResponse<Product>
{
Data = prodData,
Success = true ,
Message = ""
};
_productService.Setup(x => x.GetProductAsync(productId)).ReturnsAsync(prductResponse);
//Act
var product = await _productController.GetProduct(productId);
//Assert
Assert.That(product?.Value?.Data?.Id, Is.EqualTo(productId));
}
}
This behavior is observed due to overloaded operators on ActionResult<T> class.
Since the method, OK(value) returns an instance of OKObjectResult and the return type of controller method is of type ActionResult<ServiceResponse<Product>> the returned OKObjectResult instance is wrapped in an instance of ActionResult<T> and is exposed by the Result property. Hence typecasting the Result property (as shown by #BennyM) to OKObjectResult works.
Please note that the assertion would have succeeded had the controller method returned the ServiceResponse<Product> directly without modifying the return type on controller's method.
While this explains the behavior, I personally feel there is a better way to test controllers. This MSDN Article explains about integration testing. One can effectively unit test all the dimensions of the controllers - authentication, validation, (de)serialization, etc - by mocking the immediate dependencies of the respective controllers.
Since you are returning with an Ok call in your controller you can add a cast to the unit test.
var result = (await _productController.GetProduct(productId)).Result as OkObjectResult;
Assert.IsNotNull(result);
var returnedServiceResponse = result.Value as ServiceResponse<Product>;
Assert.That(returnedServiceResponse ?.Data?.Id, Is.EqualTo(productId));
Also you don't have to use Actionresult. You can also just return your service response
[HttpGet("{productId}")]
public async Task<ServiceResponse<Product>> GetProduct(int productId)
{
var result = await _productService.GetProductAsync(productId);
return result;
}
This will also make the test a bit easier as no need to use the OkObjectResult.

.Net Core Web API to return different data models to different clients

I have Web API developed in .Net core. I want to return different data model from the same Action method for different clients.
You can change the result of actions based on different options, but clients would be weird and I never see someone or a project that would do this, it will make the debug harder.
When a service works, it always should expose expected behavior, we should know when it's successful it give us a person object, when it fails, it return a failure message, changing the frame for clients is the worst scenario.
A better way to meet this requirement would be different API, when clients need different result, we must expose different API and these separate APIs should respect to the above rule.
You can return any model you want from one endpoint by declaring return type as Task<IActionResult>.
Suppose you have a CustomersController, so GET endpoint will be api/customers?clientType=client1. Now you want customer's different information for a different based on clientType parameter.
namespace walletapi.Controllers
{
[ApiController]
[Authorize]
public class CustomersController : ControllerBase
{
public async Task<IActionResult> Get(string clientType)
{
if(clientType=="type1"){
var type1Response = new CustomerInfoForClient1() {Property1="value1"};
return Ok(type1Response );
}
if(clientType=="type2"){
var type2Response = new CustomerInfoForClient2() {Property1="value2"};
return Ok(type2Response);
}
return NotFound("client type is not found");
}
}
public class CustomerInfoForClient1
{
public string Property1{get;set;}
}
public class CustomerInfoForClient2
{
public string Property3{get;set;}
}
}
If you are not developing microservices, usually it is not good idea having multiple result set in one endpoint. But if you need you can use IActionResult Type . With this type you don't have to declare a fixed return type. You can use like this.
[HttpGet("list/{clientType}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult ReturnSomeList(int clientType)
{
var firstList = new List<string>();
for (int i = 0; i < 3; i++)
{
firstList.Add($"str {i}");
}
var secondList = new List<int>();
for (int i = 0; i < 5; i++)
{
secondList.Add(i);
}
if (clientType == 0)
{
return Ok(firstList);
}
if (clientType == 1)
{
return Ok(secondList);
}
return NotFound("Can not find something");
}

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.

Web Api interaction between multiple controller endpoints

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.

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