Error with IHttpActionResult in web api actions - c#

I am facing method not found error in web server, but locally in visual studio it works:
[HttpGet]
[Route("api/checkhealth")]
public async Task<IHttpActionResult> CheckHealth()
{
var message = "checkhealth method was invoked";
return new TextResult(message, Request);
}
Then in browser getting below error:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>Method not found: 'System.Net.Http.HttpRequestMessage System.Web.Http.ApiController.get_Request()'.
</ExceptionMessage>
<ExceptionType>System.MissingMethodException</ExceptionType>
<StackTrace>at BMI.Controllers.APIController.<CheckHealth>d__0.MoveNext() at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine) at BMI.Controllers.APIController.CheckHealth() at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_3.<GetExecutor>b__2(Object instance, Object[] methodParameters) at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ApiControllerActionInvoker.
<InvokeActionAsyncCore>d__1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.
<ExecuteAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.
<SendAsync>d__15.MoveNext()
</StackTrace>
</Error>
I have implemented IHttpActionResult as below:
public class TextResult : IHttpActionResult
{
string message;
HttpRequestMessage request;
public TextResult(string message, HttpRequestMessage request)
{
this.message = message;
this.request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage()
{
Content = new StringContent(message),
RequestMessage = request
};
return Task.FromResult(response);
}
}
The actual method in my project is post but here I am trying to fix with get first then I believe post will also work.
Here to mention the below method work perfectly, so I think something I am missing with IHttpActionResult:
[HttpGet]
[Route("api/getok")]
public JsonResult<string> getJson()
{
return Json("OK");
}
Do you have any one faced and solved this problem yet. Please help me, thanks in advance.

Using the CreateResponse extension on the request would allow any configuration from the request to be copied over to the response which would probably be missing when you create the response manually like in your example.
public class TextResult : IHttpActionResult {
string message;
HttpRequestMessage request;
public TextResult(string message, HttpRequestMessage request) {
this.message = message;
this.request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
var response = request.CreateResponse(HttpStatusCode.OK, message);
return Task.FromResult(response);
}
}
Also the controller action is not defined correctly as it is defined as async Task<IHttpActionResult> when the action is not doing anything async.
Refactor to follow correct syntax if not actually asynchronous.
[HttpGet]
[Route("api/checkhealth")]
public IHttpActionResult CheckHealth() {
var message = "checkhealth method was invoked";
return new TextResult(message, Request);
}

Related

Jil.DeserializationException: 'Expected digit'

I am trying to implement the Jil serializer in WebAPI(C#). While deserializing throws the error like "Jil.DeserializationException: 'Expected digit'". I have attached the code samples.
WebApiConfig.cs
config.Formatters.Clear();
var _jilOptions = new Options(dateFormat: DateTimeFormat.ISO8601, excludeNulls: true, includeInherited: true);
config.Formatters.Add(new JilFormatter(_jilOptions));
JilFormatter.cs
public class JilFormatter : MediaTypeFormatter
{
private static readonly MediaTypeHeaderValue _applicationJsonMediaType = new MediaTypeHeaderValue("application/json");
private static readonly MediaTypeHeaderValue _textJsonMediaType = new MediaTypeHeaderValue("text/json");
private readonly Options _options;
public JilFormatter(Options options)
{
_options = options;
SupportedMediaTypes.Add(_applicationJsonMediaType);
SupportedMediaTypes.Add(_textJsonMediaType);
SupportedEncodings.Add(new UTF8Encoding(false, true));
SupportedEncodings.Add(new UnicodeEncoding(false, true, true));
}
public override bool CanReadType(Type type)
{
if (type == null)
return false;
return true;
}
public override bool CanWriteType(Type type)
{
if (type == null)
return false;
return true;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream input, HttpContent content, IFormatterLogger formatterLogger)
{
var reader = new StreamReader(input);
var deserialize = TypedDeserializers.GetTyped(type);
var result = deserialize(reader, _options);
return Task.FromResult(result);
}
public override Task WriteToStreamAsync(Type type, object value, Stream output, HttpContent content, TransportContext transportContext)
{
var writer = new StreamWriter(output);
JSON.Serialize(value, writer, _options);
writer.Flush();
return Task.FromResult(true);
}
}
TypedDeserializers class:
static class TypedDeserializers
{
private static readonly ConcurrentDictionary<Type, Func<TextReader, Options, object>> _methods;
private static readonly MethodInfo _method = typeof(JSON).GetMethod("Deserialize", new[] { typeof(TextReader), typeof(Options) });
static TypedDeserializers()
{
_methods = new ConcurrentDictionary<Type, Func<TextReader, Options, object>>();
}
public static Func<TextReader, Options, object> GetTyped(Type type)
{
return _methods.GetOrAdd(type, CreateDelegate);
}
private static Func<TextReader, Options, object> CreateDelegate(Type type)
{
return (Func<TextReader, Options, object>)_method
.MakeGenericMethod(type)
.CreateDelegate(typeof(Func<TextReader, Options, object>));
}
}
ReuestTest.cs
public class ReuestTest
{
public long UserId { get; set; }
public long MobileDeviceId { get; set; }
public bool IsInitialLoad { get; set; }
}
testController.cs
[HttpPost]
public string GetAllDispatchestest(ReuestTest request)
{
return something;
}
Exception:
{
"Message": "An error has occurred.",
"ExceptionMessage": "Expected digit",
"ExceptionType": "Jil.DeserializationException",
"StackTrace": " at Jil.Deserialize.Methods._ReadInt64(TextReader reader)\r\n at _DynamicMethod4(TextReader , Int32 )\r\n at Jil.JSON.Deserialize[T](TextReader reader, Options options)\r\n at api.myapi.com.Utilities.JilFormatter.ReadFromStreamAsync(Type type, Stream input, HttpContent content, IFormatterLogger formatterLogger) in F:\\Projects\\myapi\\myapi.Solution\\api.myapi.com\\Utilities\\JilFormatter.cs:line 51\r\n at System.Net.Http.Formatting.MediaTypeFormatter.ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpContentExtensions.<ReadAsAsyncCore>d__0`1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

Injected service not resolved only for Delete action

I have a controller the beginning of which looks like this:
[Route("api/[controller]")]
public class PersonsController : BaseController
BaseController looks like this so far:
public class BaseController : Controller
{
public BaseController(CompanyDbContext dbContext)
{
Db = dbContext;
}
protected CompanyDbContext Db;
}
PersonsController has a few gets, a post, a put, and a delete. Everything except the Delete action works fine, there are no problems resolving the injected CompanyDbContext.
The Delete action looks like this:
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var pers = await Db.Persons.SingleOrDefaultAsync(p => p.PersonId == id);
if (pers == null)
{
return NotFound();
}
Db.Remove(pers);
await Db.SaveChangesAsync();
return Ok();
}
Yet when I do a delete request:
public async Task DeletePersonAsync(int id)
{
var resp = await _client.DeleteAsync($"api/Persons/{id}");
resp.EnsureSuccessStatusCode();
}
where _client is an HttpClient, I get the following error:
InvalidOperationException: Unable to resolve service for type
'AcmeSoft.Api.Data.CompanyDbContext' while attempting to activate
'AcmeSoft.Api.Controllers.PersonsController'
I am using the default, built-in DI that comes with a new MVC Core 2.0 project. I register the context service in class Startup as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CompanyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
Yet the service resolves properly for all actions but Delete. What could be wrong here? Where have I gone wrong?
For those that like long questions, the entire PersonsController is as follows:
[Route("api/[controller]")]
public class PersonsController : BaseController
{
public PersonsController(CompanyDbContext dbContext) : base(dbContext)
{
}
[HttpGet]
public IActionResult Get()
{
var persons = Db.Persons.ToList();
return Ok(persons);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var pers = await Db.Persons.SingleOrDefaultAsync(p => p.PersonId == id);
if (pers == null)
{
return Ok(null);
}
return Ok(pers);
}
[HttpGet("GetByIdNumber/{idNumber}")]
public async Task<IActionResult> GetByIdNumber(string idNumber)
{
var pers = await Db.Persons.SingleOrDefaultAsync(e => e.IdNumber == idNumber);
if (pers == null)
{
return Ok(null);
}
return Ok(pers);
}
[HttpGet("PersonEmployees")]
public async Task<IActionResult> PersonEmployees()
{
var emps = await Db.PersonEmployees.ToListAsync();
return Ok(emps);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] Person person)
{
Db.Add(person);
await Db.SaveChangesAsync();
return Ok(person);
}
[HttpPut]
public async Task<IActionResult> Put([FromBody] Person person)
{
Db.Update(person);
await Db.SaveChangesAsync();
return Ok(person);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var pers = await Db.Persons.SingleOrDefaultAsync(p => p.PersonId == id);
if (pers == null)
{
return NotFound();
}
Db.Remove(pers);
await Db.SaveChangesAsync();
return Ok();
}
}
All actions except delete work.
ADDED: The stack trace shown on the developer error page is:
System.InvalidOperationException: Unable to resolve service for type 'AcmeSoft.Api.Data.CompanyDbContext' while attempting to activate 'AcmeSoft.Api.Controllers.PersonsController'.
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeInnerFilterAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeFilterPipelineAsync>d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.<InvokeAsync>d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()

Asp.net Unit test for methods with IHttpActionResult return type and Mapped data

Hello I need to test my APIcontroller which implements ApiController and I don't know how to do this, I know basics of UnitTesting but this is a little bit too complex for me. Also I don't know how to use automapper in unit tests, so maybe someone could help me with this
Here is my controller:
namespace Vidly.Controllers.Api
{
public class CustomersController : ApiController
{
private ApplicationDbContext _context;
public CustomersController(ApplicationDbContext _context)
{
_context = new ApplicationDbContext();
}
// GET /api/customers
public IHttpActionResult GetCustomers(string query = null)
{
var customersQuery = _context.Customers
.Include(c => c.MembershipType);
if (!String.IsNullOrWhiteSpace(query))
customersQuery = customersQuery.Where(c => c.Name.Contains(query));
var customerDtos = customersQuery
.ToList()
.Select(Mapper.Map<Customer, CustomerDto>);
return Ok(customerDtos);
}
// GET /api/customers/1
public IHttpActionResult GetCustomer(int id)
{
var customer = _context.Customers.SingleOrDefault(c => c.Id == id);
if (customer == null)
return NotFound();
return Ok(Mapper.Map<Customer, CustomerDto>(customer));
}
// POST /api/customers
[HttpPost]
public IHttpActionResult CreateCustomer(CustomerDto customerDto)
{
if (!ModelState.IsValid)
return BadRequest();
var customer = Mapper.Map<CustomerDto, Customer>(customerDto);
_context.Customers.Add(customer);
_context.SaveChanges();
customerDto.Id = customer.Id;
return Created(new Uri(Request.RequestUri + "/" + customer.Id), customerDto);
}
// PUT /api/customers/1
[HttpPut]
public IHttpActionResult UpdateCustomer(int id, CustomerDto customerDto)
{
if (!ModelState.IsValid)
return BadRequest();
var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);
if (customerInDb == null)
return NotFound();
Mapper.Map(customerDto, customerInDb);
_context.SaveChanges();
return Ok();
}
// DELETE /api/customers/1
[HttpDelete]
public IHttpActionResult DeleteCustomer(int id)
{
var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);
if (customerInDb == null)
return NotFound();
_context.Customers.Remove(customerInDb);
_context.SaveChanges();
return Ok();
}
}
}
So how do I need to start, do I need some kind of interface like this to mock dbcontext:
public interface IAPICustomerRepository
{
IHttpActionResult GetCustomers(string query = null);
IHttpActionResult GetCustomer(int id);
IHttpActionResult CreateCustomer(CustomerDto customerDto);
IHttpActionResult UpdateCustomer(int id, CustomerDto customerDto);
IHttpActionResult DeleteCustomer(int id);
}
Or maybe I can write Unit tests without mocking.
UPDATE
After I edited my code with Nkosi's suggestion I am getting these errors
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
An error occurred when trying to create a controller of type 'CustomersController'. Make sure that the controller has a parameterless public constructor.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'Vidly.Controllers.Api.CustomersController' does not have a default constructor
</ExceptionMessage>
<ExceptionType>System.ArgumentException</ExceptionType>
<StackTrace>
at System.Linq.Expressions.Expression.New(Type type)
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</InnerException>
</Error>
Then I create a default constructor (without parameters as I understand) and then I get another error:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Object reference not set to an instance of an object.
</ExceptionMessage>
<ExceptionType>System.NullReferenceException</ExceptionType>
<StackTrace>
at Vidly.Controllers.Api.CustomersController.GetCustomers(String query) in C:\Users\Dovydas Petrutis\Desktop\vidly-mvc-5-master\Vidly\Controllers\Api\CustomersController.cs:line 26
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[]
methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher <SendAsync>d__1.MoveNext()
</StackTrace>
</Error>
Where could be the problem now?
First create a service that exposes the functionality you want so that it can be easily replaced/mocked for testing and improve maintainability.
You were close when you mentioned the repository. IHttpActionResult is a UI concern so you can refactor your interface like this.
public interface IAPICustomerRepository {
IEnumerable<CustomerDto> GetCustomers(string query = null);
CustomerDto GetCustomer(int id);
int CreateCustomer(CustomerDto customerDto);
CustomerDto UpdateCustomer(int id, CustomerDto customerDto);
bool? DeleteCustomer(int id);
}
The controller will now be slimmer in terms of functionality and no longer cares about EF/DbContext or Automapper.
public class CustomersController : ApiController {
private IAPICustomerRepository repository;
public CustomersController(IAPICustomerRepository repository) {
this.repository = repository;
}
// GET /api/customers
public IHttpActionResult GetCustomers(string query = null) {
var customerDtos = repository.GetCustomers(query);
return Ok(customerDtos);
}
// GET /api/customers/1
public IHttpActionResult GetCustomer(int id) {
var customer = repository.GetCustomer(id);
if (customer == null)
return NotFound();
return Ok(customer);
}
// POST /api/customers
[HttpPost]
public IHttpActionResult CreateCustomer([FromBody]CustomerDto customerDto) {
if (!ModelState.IsValid)
return BadRequest();
var id = repository.CreateCustomer(customerDto);
customerDto.Id = id;
return Created(new Uri(Request.RequestUri + "/" + id), customerDto);
}
// PUT /api/customers/1
[HttpPut]
public IHttpActionResult UpdateCustomer(int id, [FromBody]CustomerDto customerDto) {
if (!ModelState.IsValid)
return BadRequest();
var updated = repository.UpdateCustomer(id, customerDto);
if (updated == null)
return NotFound();
return Ok();
}
// DELETE /api/customers/1
[HttpDelete]
public IHttpActionResult DeleteCustomer(int id) {
var deleted = repository.DeleteCustomer(id);
if (deleted == null)
return NotFound();
return Ok();
}
}
With the controller now dependent on an abstraction you can mock the functionality when testing the controller in isolation.
Automapper is an implementation concern that can be encapsulated behind the abstraction so that it is a non issue when testing.
The following example uses Moq mocking framework. You can use your framework of choice is you so choose.
[TestClass]
public class CustomersController_Should {
[TestMethod]
public void GetCustomers() {
//Arrange
var fakeCustomers = new List<CustomerDto>{
new CustomerDto{ Id = 1 }
};
var repository = new Mock<IAPICustomerRepository>();
repository
.Setup(_ => _.GetCustomers(It.IsAny<string>()))
.Returns(fakeCustomers)
.Verifiable();
var controller = new CustomersController(repository.Object);
//Act
var result = controller.GetCustomers();
//Assert
repository.Verify();
//..other assertions
}
//...Other tests
}
The functionality that was originally in the controller would be encapsulated in the implementation of the repository in production.

Property null in web api controller when using Entity Framework 5 and Oracle

i have a problem in a new web api controller when i try to use EF5 with Oracle.
My controller:
public class DeviceV1Controller : ApiController
{
private readonly IDevice _repository;
public DeviceV1Controller()
{
IDevice _repository = new EFDeviceRepository();
}
[Route("api/Device/{hashImei}/app/{nome}")]
public HttpResponseMessage Post(string hashImei, string nome, [FromBody] DeviceInfo deviceInfo)
{
_repository.Add(deviceInfo);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
_repository is correctly bound in the constructor, but entering the Post api this variable become null and i get this error:
{
message: "An error has occurred."
exceptionMessage: "object reference not set to an instance of an object."
exceptionType: "System.NullReferenceException"
stackTrace: " in MpssApiRest.Controllers.DeviceV1Controller.Post(String hashImei, String nome, DeviceInfo deviceInfo) in c:\SVILUPPO\MpssApiRest\MpssApiRest\MpssApiRest\Controllers\DeviceV1Controller.cs:riga 28 in lambda_method(Closure , Object , Object[] ) in System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters) in System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) in System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- Fine traccia dello stack da posizione precedente dove è stata generata l'eccezione --- in System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) in System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) in System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- Fine traccia dello stack da posizione precedente dove è stata generata l'eccezione --- in System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) in System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) in System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- Fine traccia dello stack da posizione precedente dove è stata generata l'eccezione --- in System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) in System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) in System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()"
}
EFDeviceRepository concrete class is:
public class EFDeviceRepository : IDevice
{
private readonly EntityDevice ctx;
public EFDeviceRepository()
{
ctx = new EntityDevice();
}
public void Add(Models.V1.DeviceInfo deviceInfo)
{
EntityDevice ctx = new EntityDevice();
MPSS_APP_DEVICE device = new MPSS_APP_DEVICE();
device.HASHIMEI = deviceInfo.HashImei;
ctx.MPSS_APP_DEVICE.Add(device);
ctx.SaveChanges();
}
}
Thanks
EDIT: Sample Web Request (retrieved from the comments)
Ip Address: 192.168.1.129
Url: /myproject/api/device/123456/app/appname
JSON:
{
"applicazione" : "Gestione Interventi",
"hashImei" : "123123121323123121",
"modello" : "Nexus 5",
"pushNotificatioToken" : "oifjwfijowfjfoiwjrgfoirwj42rohfoifrj",
"sistemaOperativo" : "ANDROID", "versione" : "LOLLIPOP_MR1"
}
The reason why _repository is null in your Action is because you are not initializing it in your constructor. Instead you have declared and initialized a local variable of the same name in your constructor!
public class DeviceV1Controller : ApiController
{
private readonly IDevice _repository;
public DeviceV1Controller()
{
_repository = new EFDeviceRepository();
}
// ...
}

After first request HttpClientHandler is disposed

I want to send two request from my ViewModel (first - GET and then - POST) using HttpClient. GET request completes without any error. But if then I send POST request I got exception:
{System.ObjectDisposedException: The object has been closed. (Exception from HRESULT: 0x80000013)} System.Exception {System.ObjectDisposedException}
Or if I ran POST request before GET - POST completes ok and GET - fails with the same exception.
I'm using one HttpClientHandler for both requests (becouse I store Cookies in that HttpClientHandler)
public async Task<CategoryGroupModel> GetCategoryGroup(int categoryGroupId)
{
var handler = new HttpClientHandler {CookieContainer = App.Cookies};
using (var client = new MmcHttpClient(handler))
{
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/categorygroups/" + categoryGroupId);
if (response.IsSuccessStatusCode)
{
var resultAsString = await response.Content.ReadAsStringAsync();
var jsonResult = JObject.Parse(resultAsString);
var wsResponse = jsonResult.ToObject<WebServiceResponse<CategoryGroupModel>>();
if (wsResponse.Status == HttpStatusCode.OK)
{
return wsResponse.Result;
}
else
{
throw new Exception();
}
}
else
{
throw new Exception();
}
}
}
public async Task<CategoryGroupModel> CreateCategoryGroup(CategoryGroupModel categoryGroup)
{
var handler = new HttpClientHandler {CookieContainer = App.Cookies};
using (var client = new MmcHttpClient(handler))
{
var response = await client.PostAsJsonAsync("api/categorygroups", categoryGroup);
if (response.IsSuccessStatusCode)
{
var resultAsString = await response.Content.ReadAsStringAsync();
var jsonResult = JObject.Parse(resultAsString);
var wsResponse = jsonResult.ToObject<WebServiceResponse<CategoryGroupModel>>();
if (wsResponse.Status == HttpStatusCode.OK)
{
return wsResponse.Result;
}
else
{
throw new Exception();
}
}
else
{
throw new Exception();
}
}
}
MmcHttpClient:
public class MmcHttpClient : HttpClient
{
public MmcHttpClient(HttpClientHandler handler) : base(App.Handler)
{
BaseAddress = new Uri("http://localhost:65066/");
DefaultRequestHeaders.Accept.Clear();
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
StackTrace:
System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Http.HttpClientHandler'.
at System.Net.Http.HttpClientHandler.CheckDisposed()
at System.Net.Http.HttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.PostAsync(String requestUri, HttpContent content, CancellationToken cancellationToken)
at System.Net.Http.HttpClientExtensions.PostAsync[T](HttpClient client, String requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken)
at System.Net.Http.HttpClientExtensions.PostAsync[T](HttpClient client, String requestUri, T value, MediaTypeFormatter formatter, CancellationToken cancellationToken)
at System.Net.Http.HttpClientExtensions.PostAsJsonAsync[T](HttpClient client, String requestUri, T value, CancellationToken cancellationToken)
at System.Net.Http.HttpClientExtensions.PostAsJsonAsync[T](HttpClient client, String requestUri, T value)
at MMCClient.Repositories.CategoryGroupRepository.<CreateCategoryGroup>d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at MMCClient.ViewModels.CategoryGroupViewModel.<Create>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__3(Object state)
at System.Threading.WinRTSynchronizationContext.Invoker.InvokeCore()} System.Exception {System.ObjectDisposedException}
It was my fault. After adding second param (disposeHandler) to HttpClient constructor all works good:
public class MmcHttpClient : HttpClient
{
public MmcHttpClient(HttpClientHandler handler, bool disposeHandler) :
base(App.Handler, disposeHandler)
{
BaseAddress = new Uri("http://localhost:65066/");
DefaultRequestHeaders.Accept.Clear();
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
You can read about it here

Categories

Resources