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);
}
}
Related
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
I have NET Core 2 Web API application. During the process i have to invoke Client A's API to get some data. So i am using HttpClient to invoke it. Client A also requires me to pass userid and password in header.
So instead of directly injecting HttpClient i have wrapper around HttpClient something like below
public class ClientA : IClientA
{
private readonly HttpClient _httpClient;
public ClientA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetData()
{
return await _httpClient.HttpGetAsync("someurl");
}
}
Then use ClientA in Service
public class MyService :IMyService
{
private readonly IClientA _clientA
public MyService(IClientA clientA)
{
_clientA= clientA
}
public void DoSomethig()
{
_clientA.GetData();
}
}
Then i am registering everything in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
services.AddScoped(factory =>
{
Func<Task<IClientA>> provider = async () =>
{
using (var dbContext = factory.GetService<MyDBContext>())
{
// get userid and password from database here
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("UserId",userid);
httpClient.DefaultRequestHeaders.Add("Password",password);
return new ClientA(httpClient);
}
};
return provider;
});
}
However i am getting error
System.InvalidOperationException: Unable to resolve service for type
'System.Net.Http.HttpClient' while attempting to activate
'XXXXXXXXX.ClientA'. at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, ISet1 callSiteChain,
ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type
serviceType, Type implementationType, ISet1 callSiteChain)
remaining exception removed for brevity
Notice that during registration i am newing-up instance of HttpClient and passing it to ClientA class because i have to set userid and password.
To get rid the above error I can register HttpClient with UserID and Password with DI framework and i guess that would work.
However, in that case, if have another client, ClientB, that takes HttpClient then DI framework will inject same httpclient that has userid and password. and that will create security issue because ClientB would see ClientA's credentials in request headers.
public class ClientB(HttpClient client)
{
private readonly _httpClient;
public class ClientB(HttpClient client)
{
_httpClient = client;
}
public string CallClientB(string url)
{
// here ClientB will receive ClientA's credentials
return await _httpClient.HttpGetAsync(url);
}
}
You don't want to be instantiating httpclient in a scoped context, that is creating an instance of httpclient per request which is not the recommended usage pattern for that class. (won't scale well). https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Create a singleton with a separate interface per customer (assuming a small number of customers) - possibly with a code access security demand in its implementation, depending on your setup (identity impersonation enabled?)
That will a) scale well b) only run once per customer per application instance/startup and c) enforce an access check for usage.
Also, this answer is connected and relevant to your header requirements - HttpClient single instance with different authentication headers
resolved my issue
services.AddScoped<IClientA>(factory =>
{
var dbContext = factory.GetService<MyDBContext>();
// get userid and password from database here
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("UserId",userid);
httpClient.DefaultRequestHeaders.Add("Password",password);
return new ClientA(httpClient);
});
I have an wcf SERVICE that get a request of branch code and return 5 products list values.
In server side, each product process takes about 6 sec to be completed, so if I run synchronous it will take 30 seconds. I altered my server code and run them in parallel. It works fine, but it seems that each parallel process get threads from IIS threadspool.
Now I am thinking to run asynchronous each product processes and when all will be finished then return the whole list of products to client. It will be grateful if you help me about that..
How can I run asynchronous these processes in server side ? No matter if client run sync or async. That what I need is to run async product processes in order to serve the results faster.
Try this asynchronous approach with generics type:
namespace InterNameSpace
{
[ServiceContract]
public interface ITestClass
{
[OperationContract]
Task<AnyComplexType> xpto(string baseUrl, string getAsyncUrl);
}
}
namespace Test
{
ServiceBehaviorAttribute(InstanceContextMode = InstanceContextMode.PerSession)]
public class TestClass : ITestClass
{
public async Task<AnyComplexType> xpto(string baseUrl, string getAsyncUrl)
{
try
{
return await TestAux.Auxiliar.RetrieveDeserializeObject<AnyComplexType>(baseUrl, getAsyncUrl);
}
catch (Exception)
{
return null;
}
}
}
}
Create a Product complex-type, pass it to your call method. In this method use the keyword async and task to call the deserialized method as asynchronous.
Your deserialized method recieve the baseurl and the controller route. As a genetic type you can pass different complex-type and deserialaze this object and return it.
namespace TestAux
{
public class Auxiliar
{
public static async Task<T> RetrieveDeserializeObject<T>(string baseUrl, string getAsyncUrl) where T : class
{
try
{
T deserializeObject = null;
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(baseUrl);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.GetAsync(getAsyncUrl);
//throw an exception if not successful
if (response.EnsureSuccessStatusCode().IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
// DeserializeObject our concrete class into a JSON String
deserializeObject = await Task.Run(() => JsonConvert.DeserializeObject<T>(content));
}
return deserializeObject;
}
catch (Exception)
{
return null;
}
}
}
}
There is no easy way to get an access to a CookieContainer in response object running integration tests with AspNet.TestHost.TestServer. Cookies have to be set by the controller action. What is the best way to achieve that?
var client = TestServer.Create(app =>
{
app.UseMvc(routes =>
routes.MapRoute("default", "{controller}/{action}/{id?}"));
app.UseIdentity();
}).CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "account/login");
var response = await client.SendAsync(request);
// how to get an access to cookie container ?????????
// response.Cookies prop doesn't exist
Assert.NotEmpty(response.Cookies["auth"]);
Solution that I see is to extend instance of the TestServer, return instance of a class CustomClientHandler : ClientHandler and override the whole process of sending a request in that handler, but it needs literally to change all logic except relatively small code of the TestServer.
Any better suggestion how to implement an access to Cookies in a response?
As an addition to #Oleh's response, you can achieve the same without reflection on newer frameworks like .NET 4.6.1+ / .NET Core
public class TestHttpClientHandler : DelegatingHandler
{
[NotNull]
private readonly CookieContainer cookies = new CookieContainer();
public TestHttpClientHandler([NotNull] HttpMessageHandler innerHandler)
: base(innerHandler) { }
[NotNull, ItemNotNull]
protected override async Task<HttpResponseMessage> SendAsync([NotNull] HttpRequestMessage request, CancellationToken ct)
{
Uri requestUri = request.RequestUri;
request.Headers.Add(HeaderNames.Cookie, this.cookies.GetCookieHeader(requestUri));
HttpResponseMessage response = await base.SendAsync(request, ct);
if (response.Headers.TryGetValues(HeaderNames.SetCookie, out IEnumerable<string> setCookieHeaders))
{
foreach (SetCookieHeaderValue cookieHeader in SetCookieHeaderValue.ParseList(setCookieHeaders.ToList()))
{
Cookie cookie = new Cookie(cookieHeader.Name.Value, cookieHeader.Value.Value, cookieHeader.Path.Value);
if (cookieHeader.Expires.HasValue)
{
cookie.Expires = cookieHeader.Expires.Value.DateTime;
}
this.cookies.Add(requestUri, cookie);
}
}
return response;
}
}
I've implemented custom HttpMessageHandler that tracks cookies.
It uses reflection to invoke the actual handler and just reads/sets Cookie headers.
class TestMessageHandler : HttpMessageHandler
{
delegate Task<HttpResponseMessage> HandlerSendAsync(HttpRequestMessage message, CancellationToken token);
private readonly HandlerSendAsync nextDelegate;
private readonly CookieContainer cookies = new System.Net.CookieContainer();
public TestMessageHandler(HttpMessageHandler next)
{
if(next == null) throw new ArgumentNullException(nameof(next));
nextDelegate = (HandlerSendAsync)
next.GetType()
.GetTypeInfo()
.GetMethod("SendAsync", BindingFlags.NonPublic | BindingFlags.Instance)
.CreateDelegate(typeof(HandlerSendAsync), next);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("Cookie", cookies.GetCookieHeader(request.RequestUri));
var resp = await nextDelegate(request, cancellationToken).ConfigureAwait(false);
if (resp.Headers.TryGetValues("Set-Cookie", out var newCookies))
{
foreach (var item in SetCookieHeaderValue.ParseList(newCookies.ToList()))
{
cookies.Add(request.RequestUri, new Cookie(item.Name, item.Value, item.Path));
}
}
return resp;
}
}
And then you create your HttpClient like this:
var httpClient = new HttpClient(
new TestMessageHandler(
server.CreateHandler()));
TestMessageHandler now takes care of tracking cookies.
For a dotnet core integration test approach like the one described in the docs here, you can get cookies with the following code:
public class CookieTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
public CookieTests(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task GetPage_ShouldSetCookie_CookieSet()
{
using (var client = _factory.CreateClient())
{
var response = await client.GetAsync("/cookie_setting_url");
response.EnsureSuccessStatusCode();
//or other assertions
Assert.True(response.Headers.TryGetValues(HeaderNames.SetCookie, out IEnumerable<string> cookies));
}
}
}
The proper way, using minimal code getting cookies
in Asp.Net Core Functional Tests is as follows, (I leave out init code for setting up WebApplicationFactory, which is known stuff)
The given examples above, require either reflection (Since I think MS made a design bug on not exposing the default handlers) or require cookie parsing, which is annoying in 2023.
private (HttpClient, CookieContainerHandler) GetHttpClient()
{
CookieContainerHandler cookieContainerHandler = new();
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddAuthentication(defaultScheme: "YourSchema")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
"TestAzure", options => { });
});
}).CreateDefaultClient(cookieContainerHandler);
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "YourSchema");
return (client, cookieContainerHandler);
}
[Fact]
public async Task MyUnitTest()
{
// Arrange
var (client, cookieHandler) = GetHttpClient();
// Act PUT/GET/POST etc
var response = await client.PutAsync("youruri", null);
var sessionCookie = cookieHandler.Container.GetAllCookies().FirstOrDefault(f => f.Name == "yourcookie"); // note this ignores cookie domain policy
}
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;