How to mock WepAPi response (JSON) in client MVC client project - asp.net-mvc-4

I want to test my service proxy class that is calling a MVC WebApi and gets back a JSON response.
public class CarServiceProxy
{
public CarsCollection GetCars()
{
CarsCollection cars = new CarsCollection();
string api = "api/Car/GetCars";
var response = httpClient.GetAsync(api).Result;
if (response.IsSuccessStatusCode)
{
cars = response.Content.ReadAsAsync<CarsCollection >().Result;
}
return cars;
}
}
then my MVC WebSite controller class calls the above proxy class as:
public ActionResult Index()
{
CarsCollection cars = this.carsServiceProxy.GetCars();
return View(cars);
}
Now to test both in isolation, I can test my controller using MOQ and mocking carServiceProxy and faking the CarsCollection with some fake data in my tests. I am ok so far.
But, how do I test the proxy class using MOQ to mock the API response (JSON)? I think I do need to test because the conversion form JSON to C# happens in that class, and if some one changes my model CarsCollection, that might break. So I do need to test the proxy class.

You will want to create a "Fake" HTTP handler for your client. This way you can control exactly what HTTP response is returned. You can inject an HTTP handler when you construct an HTTP client.
For testing I typically use an internal constructor for testing, which accepts an HTTP handler. This way I can easily test my class that consumes an HTTP Client with a fake handler. If you take this approach and your unit tests are in a separate assembly, you'll need to add the following to the AssemblyInfo.cs for your target project:
[assembly: InternalsVisibleTo("NameSpace.MyUnitTestProject")]
Fake Handler:
public class FakeHttpMessageHandler : HttpMessageHandler
{
private HttpResponseMessage _response;
public FakeHttpMessageHandler(HttpResponseMessage response)
{
_response = response;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var responseTask = new TaskCompletionSource<HttpResponseMessage>();
responseTask.SetResult(_response);
return responseTask.Task;
}
}
And then to consume (may want to use JSON serialisation helpers here):
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(#"{'Cars':[{'Name':'BMW'}]");
var handler = new FakeHttpMessageHandler(response);
var client = new HttpClient(handler);
// client.GetAsync().result will return the response

You can change the httpClient.GetAsync(api).Result part to be moackable and testable to test the proxy class. In other words, you can make your code independent of HTTP request response and just work on JSON data for testing.

Related

Why can't my xamarin app recieve data from my API call?

Im new to connecting an API to my xamarin app.
When I try to call the API visual studio & the app do not give a response.
Visual studio keeps running but nothing happens.
I've changed the firewall settings, and set my IP adres in all the desired places. Still not luck.
If I go to my API using swager or postman and I just the same Uri as I want to pass trough with my app I get the correct response.
What could be the reason for this?
my code:
Material service:
private readonly string _baseUri;
public APIMaterialService()
{
_baseUri = "https://192.168.1.9:5001/api";
}
public async Task<Material> GetById(Guid id)
{
return await WebApiClient
.GetApiResult<Material>($"{_baseUri}/Materials/{id}");
}
WebApiClient:
public class WebApiClient
{
private static HttpClientHandler ClientHandler()
{
var httpClientHandler = new HttpClientHandler();
#if DEBUG
//allow connecting to untrusted certificates when running a DEBUG assembly
httpClientHandler.ServerCertificateCustomValidationCallback =
(message, cert, chain, errors) => { return true; };
#endif
return httpClientHandler;
}
private static JsonMediaTypeFormatter GetJsonFormatter()
{
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return formatter;
}
public async static Task<T> GetApiResult<T>(string uri)
{
using (HttpClient httpClient = new HttpClient(ClientHandler()))
{
//Gets stuck finding the response
string response = await httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(response, GetJsonFormatter().SerializerSettings);
}
}
I'll also add some images of the postman and swager response:
This is the code fo my controller.
return OK (material) shows me the data retrieved from the API
public async Task<IActionResult> GetMaterialByPartOfMaterialNumberOP(string partOfMaterialNumber)
{
var material = await _materialService.GetMaterialListbyPartOfMaterialNumber(partOfMaterialNumber);
return Ok(material);
}
The symptom you have (stuck on result from calling a method of HttpClient class) suggests a deadlock.
I believe the deadlock happens if you create multiple instances of HttpClient.
Doc HttpClient Class says:
// HttpClient is intended to be instantiated once per application, rather than per-use. See Remarks.
And shows this code:
static readonly HttpClient client = new HttpClient();
HOWEVER a deadlock would only happen the SECOND time your code does new HttpClient. And using ... new HttpClient should protect you, at least in simple situations.
Here are ways there might be TWO HttpClients active:
Is it possible that GetApiResult gets called A SECOND TIME, before the first one finishes?
Does your app do new HttpClient ELSEWHERE?
Here is what the technique might look like in your app:
public class WebApiClient
{
static HttpClient _client = new HttpClient(ClientHandler());
public async static Task<T> GetApiResult<T>(string uri)
{
string response = await _client.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(response, GetJsonFormatter().SerializerSettings);
}
}

Generic passthrough/forwarding of form data in ApsNet.Core

I'm attempting to create a webhook to receive messages from a Twilio phone number. But instead of just needing a webhook that will modify the data and immediately return a result to Twilio, I need this webhook to pass Twilio's message into an internal API, wait for the response, and then return the result to Twilio.
Here's some generic code I came up with that I hoped would work.
public async Task<HttpResponseMessage> ReceiveAndForwardSms(HttpContent smsContent)
{
var client = new HttpClient();
HttpResponseMessage response = await client.PostAsync(Environment.GetEnvironmentVariable("requestUriBase") + "/api/SmsHandler/PostSms", smsContent);
return response;
}
The problem with this code is that Twilio immediately returns a 415 error code (Unsupported Media Type) before entering the function.
When I try to accept the "correct type" (Twilio.AspNet.Common.SmsRequest), I am unable to stuff the SmsRequest back into a form-encoded object and send it via client.PostAsync()...
Ex.:
public async Task<HttpResponseMessage> ReceiveAndForwardSms([FromForm]SmsRequest smsRequest)
{
var client = new HttpClient();
var stringContent = new StringContent(smsRequest.ToString());
HttpResponseMessage response = await client.PostAsync(Environment.GetEnvironmentVariable("requestUriBase") + "/api/SmsHandler/PostSms", stringContent);
return response;
}
Is there anything I can do to "mask" the function's accepted type or keep this first function generic?
How do I go about shoving this SmsRequest back into a "form-encoded" object so I can accept it the same way in my consuming service?
TLDR
Your options are:
Use an existing reverse proxy like NGINX, HAProxy, F5
Use YARP to add reverse proxy functionality to an ASP.NET Core project
Accept the webhook request in a controller, map the headers and data to a new HttpRequestMessage and send it to your private service, then map the response of your private service, to the response back to Twilio.
It sounds like what you're trying to build is a reverse proxy. It is very common to put a reverse proxy in front of your web application for SSL termination, caching, routing based on hostname or URL, etc.
The reverse proxy will receive the Twilio HTTP request and then forwards it to the correct private service. The private service responds which the reverse proxy forwards back to Twilio.
I would recommend using an existing reverse proxy instead of building this functionality yourself. If you really want to build it yourself, here's a sample I was able to get working:
In your reverse proxy project, add a controller as such:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
namespace ReverseProxy.Controllers;
public class SmsController : Controller
{
private static readonly HttpClient HttpClient;
private readonly ILogger<SmsController> logger;
private readonly string twilioWebhookServiceUrl;
static SmsController()
{
// don't do this in production!
var insecureHttpClientHandler = new HttpClientHandler();
insecureHttpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true;
HttpClient = new HttpClient(insecureHttpClientHandler);
}
public SmsController(ILogger<SmsController> logger, IConfiguration configuration)
{
this.logger = logger;
twilioWebhookServiceUrl = configuration["TwilioWebhookServiceUrl"];
}
public async Task Index()
{
using var serviceRequest = new HttpRequestMessage(HttpMethod.Post, twilioWebhookServiceUrl);
foreach (var header in Request.Headers)
{
serviceRequest.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
serviceRequest.Content = new FormUrlEncodedContent(
Request.Form.ToDictionary(
kv => kv.Key,
kv => kv.Value.ToString()
)
);
var serviceResponse = await HttpClient.SendAsync(serviceRequest);
Response.ContentType = "application/xml";
var headersDenyList = new HashSet<string>()
{
"Content-Length",
"Date",
"Transfer-Encoding"
};
foreach (var header in serviceResponse.Headers)
{
if(headersDenyList.Contains(header.Key)) continue;
logger.LogInformation("Header: {Header}, Value: {Value}", header.Key, string.Join(',', header.Value));
Response.Headers.Add(header.Key, new StringValues(header.Value.ToArray()));
}
await serviceResponse.Content.CopyToAsync(Response.Body);
}
}
This will accept the Twilio webhook request, and forward all headers and content to the private web service. Be warned, even though I was able to hack this together until it works, it is probably not secure and not performant. You'll probably have to do a lot more to get this to become production level code. Use at your own risk.
In the ASP.NET Core project for your private service, use a TwilioController to accept the request:
using Microsoft.AspNetCore.Mvc;
using Twilio.AspNet.Common;
using Twilio.AspNet.Core;
using Twilio.TwiML;
namespace Service.Controllers;
public class SmsController : TwilioController
{
private readonly ILogger<SmsController> logger;
public SmsController(ILogger<SmsController> logger)
{
this.logger = logger;
}
public IActionResult Index(SmsRequest smsRequest)
{
logger.LogInformation("SMS Received: {SmsId}", smsRequest.SmsSid);
var response = new MessagingResponse();
response.Message($"You sent: {smsRequest.Body}");
return TwiML(response);
}
}
Instead of proxying the request using the brittle code in the reverse proxy controller, I'd recommend installing YARP in your reverse proxy project, which is an ASP.NET Core based reverse proxy library.
dotnet add package Yarp.ReverseProxy
Then add the following configuration to appsettings.json:
{
...
"ReverseProxy": {
"Routes": {
"SmsRoute" : {
"ClusterId": "SmsCluster",
"Match": {
"Path": "/sms"
}
}
},
"Clusters": {
"SmsCluster": {
"Destinations": {
"SmsService1": {
"Address": "https://localhost:7196"
}
}
}
}
}
}
This configuration will forward any request to the path /Sms, to your private ASP.NET Core service, which on my local machine is running at https://localhost:7196.
You also need to update your Program.cs file to start using YARP:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
When you run both projects now, the Twilio webhook request to /sms is forwarded to your private service, your private service will respond, and your reverse proxy service will forward the response back to Twilio.
Using YARP you can do a lot more through configuration or even programmatically, so if you're interested I'd check out the YARP docs.
If you already have a reverse proxy like NGINX, HAProxy, F5, etc. it may be easier to configure that to forward your request instead of using YARP.
PS: Here's the source code for the hacky and YARP solution

Does .NET Core HttpClient have the concept of interceptors?

I would like to wrap some timing logic around all calls made through HttpClient from my ASP.NET Core app, including calls made from 3rd party libraries.
Does HttpClient in .NET Core have something I can plug into to run some code on every request?
Yes, it does. HttpClient produces a HTTP request via DelegatingHandler chain. To intercept the HttpClient request, you can add a derived handler with overrided SendAsync method to that chain.
Usage:
var handler = new ExampleHttpHandler(fooService);
var client = new HttpClient(new ExampleHttpHandler(handler));
var response = await client.GetAsync("http://google.com");
Implementation:
public class ExampleHttpHandler : DelegatingHandler
{
//use this constructor if a handler is registered in DI to inject dependencies
public ExampleHttpHandler(FooService service) : this(service, null)
{
}
//Use this constructor if a handler is created manually.
//Otherwise, use DelegatingHandler.InnerHandler public property to set the next handler.
public ExampleHttpHandler(FooService service, HttpMessageHandler innerHandler)
{
//the last (inner) handler in the pipeline should be a "real" handler.
//To make a HTTP request, create a HttpClientHandler instance.
InnerHandler = innerHandler ?? new HttpClientHandler();
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//add any logic here
return await base.SendAsync(request, cancellationToken);
}
}
BTW, I recommend moving as much business logic out of a custom handler as possible to simplify unit-testing it.

WebApiCompatShim - how to configure for a REST api with MVC 6

I was having a look at this link that shows how to migrate from Web API 2 to MVC 6.
I am trying to have Action methods in my controllers with the HttpRequestMessage bound. This works in Web Api 2.
[Route("", Name = "AddTaskRoute")]
[HttpPost]
public Task AddTask(HttpRequestMessage requestMessage, [FromBody]NewTask newTask)
{
var task = _addTaskMaintenanceProcessor.AddTask(newTask);
return task;
}
and the requestMessage contains the details about the Http request such as headers, verb, etc.
I am trying to get the same with MVC 6 but the requestMessage seems to be incorrectly bound and it shows details such as the method being GET when the action is actually a POST. I believe I haven't configured the WebApiCompatShim as per the article suggests so the binding is not properly done. But I do not have the extension method services.AddWebApiConventions(); available in the version "Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-rc1-final"
Anybody has succeed when trying this?
PS: The Request property available in my controller seems to have details about the http request, but I'd like to have the HttpRequestMessage instance.
In MVC6, You should be able to use the Request object to get header information.
var contentTypeHeader = Request.Headers["Content-Type"];
It is true that they removed some of the nice methods like Request.CreateResponse() and OK() etc.. But there are some alternatives you can use.
All of these classes we will be using to create a response are inheriting from the ObjectResult base class. So you can use ObjectResult as the return type of your Web api method.
HttpOKObjectResult
In MVC6, You can use create an object of HttpOKObjectResult class and use that as your return value instead of Request.CreateResponse(). This will produce the status code 200 OK for the response.
Web API2 code
public HttpResponseMessage Post([FromBody]string value)
{
var item = new { Name= "test", id = 1 };
return Request.CreateResponse(HttpStatusCode.OK,item);
}
MVC 6 code
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
var item = new {Name= "test", id=1};
return new HttpOkObjectResult(item);
}
Or simply use the OK() method.
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
var item = new {Name= "test", id=1};
return Ok(item);
}
CreatedAtRouteResult
You can use CreatedAtRouteResult class to send a response with 201 Created status code with a location header.
MVC 6 code
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
var item = new { Name= "test", id=250};
return new CreatedAtRouteResult(new { id = 250}, item);
}
The client will receive a location header in the response which will point to the api route with 250 as the value for the id parameter.
HttpNotFoundObjectResult
You can use this class to return a 404 Not found response.
Web API2 code
public HttpResponseMessage Post([FromBody]string value)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
MVC 6 code
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
return new HttpNotFoundObjectResult("Some");
}
I found that to use the Microsoft.AspNetCore.Mvc.WebApiCompatShim, it should be services.AddMvc().AddWebApiConventions() see this example instead of services.AddWebApiConventions() as shown in the docs.
I'm putting in a feedback item on their docs.

How to Access different methods in Web API from console Application?

How do i acces my methods in my webapi when there are multiple get,post and delete methods from my console application, how do i differentiate them this is my api controller
public IQueryable<Store> GetAll()
{
return StoreRepository.All;
}
//GetAll Stores including all relation tables
public IQueryable<Store> GetAllIncluding()
{
return StoreRepository.AllIncluding();
}
//Get store by id/id=5
public Store Find(long storeid)
{
stores = StoreRepository.Find(storeid);
return stores;
}
//Insert or Update Store
public void InsertorUpdateWithGraph(Store store)
{
StoreRepository.InsertOrUpdateWithGraph(store);
}
//Insert or Update StoreDetail
public void InsertOrUpdateStoreDetail(StoreDetail storedetail)
{
StoreRepository.InsertOrUpdateStoreDetail(storedetail);
}
//Get StoreDetail by id/id=5
public StoreDetail FindStoreDetail(long storedetailid)
{
storedetail = StoreRepository.FindStoreDetail(storedetailid);
return storedetail;
}
public List<StoreDetail> GetAllStoreDetails(long storedetailid)
{
List<StoreDetail> storedetails = StoreRepository.GetAllStoreDetails(storedetailid);
return storedetails;
}
public Sage FindSage(long sageid)
{
return StoreRepository.FindSage(sageid);
}
like this i may have more than two get,post,insert or update methods i have to acces this methods from my console application how can i map the methods i want,cana any one help me here how will i define the routes for this
You can have multiple "Get..." actions and you can get away without HttpGet attribute because they start with "Get". "Find..." methods need to be decorated with HttpGet
Those "Insert..." you need to decorate with HttpPost or HttpPut attributes.
Parameters to these methods can be configured in two ways. You can POST object like {id:"ddd",name:"nnn"} to action like
MyAction(int id, string name)
Web APi framework threats any methods that start with Post..., Delete..., Get..., Put... as corresponding Http Verbs. But you can name them the way you with and then decorate with Http attributes.
When it comes to parameters, it is about a correlation of your controller actions to the routes.
And now, to run it from the console application you can use HttpClient
string _webSiteUrl = "www.ffsdfds.com"
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_webSiteUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // for posting
HttpResponseMessage resp = httpClient.GetAsync("/api/area/getall").Result;