I use HttpClient object for PostAsync. I need to add BackgroundSessionConfiguration for iOS while I am creating HttpClient object. So I changed my code like this:
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration ("my.app.identifier");
_client = new HttpClient (new NSUrlSessionHandler (configuration));
This works when I send first request with PostAsync. But when I send request second time, it doesn't work.
I did it for Login Operation like this: (It works first time but if I logout and login again, it doesn't work.)
public class LoginService
{
private HttpClient _client;
public LoginService()
{
if (_client == null)
{
_client = Helper.CreateHttpClientLogin(_client);
}
}
public async Task<LoginResponse<LoginDataResponse>> Login(LoginRequest request)
{
LoginResponse<LoginDataResponse> responseModel = new LoginResponse<LoginDataResponse>();
try
{
string json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var jsonBody = await _client.PostAsync(App.ServiceURL.Login_Url, content);
string jsonstr = await jsonBody.Content.ReadAsStringAsync();
if (jsonstr == null || jsonstr == "")
{
responseModel.Success = false;
responseModel.Status = 0;
responseModel.Message = AppResources.UnknownHostException;
}
else
responseModel = (LoginResponse<LoginDataResponse>)JsonConvert.DeserializeObject(jsonstr, typeof(LoginResponse<LoginDataResponse>));
}
catch (Exception ex)
{
string text = ex.ToString();
responseModel.Status = 0;
AppResources.Culture = CrossMultilingual.Current.CurrentCultureInfo;
responseModel.Message = AppResources.UnknownHostException;
}
return responseModel;
}
}
public class Helper
{
public static HttpClient CreateHttpClientLogin(HttpClient _client)
{
if (Device.RuntimePlatform == Device.iOS)
{
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration("my.app.identifier");
_client = new HttpClient(new NSUrlSessionHandler(configuration));
}
else
{
//_client = new HttpClient(new System.Net.Http.HttpClientHandler());
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
_client = new HttpClient(handler);
}
return _client;
}
}
And I have this code on AppDelegate: (I don't know but maybe it causes the bug)
public static Action BackgroundSessionCompletionHandler;
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
// We get a completion handler which we are supposed to call if our transfer is done.
BackgroundSessionCompletionHandler = completionHandler;
}
What must I do for this?
Edit:
I solved the problem I mentioned above by creating the Login Service object once the application was first opened. (After logout previously, I was rebuilding every time I login)
But now I have other error. When I run my app on "iPhone 7 plus - iOS 13.6" device I got this error:
System.Net.Http.HttpRequestException: unknown error ---> Foundation.NSErrorException: Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLStringKey=https://mydomain/Api/Login, NSErrorFailingURLKey=https://mydomain/Api/Login, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>, NSLocalizedDescription=unknown error}
--- End of inner exception stack trace ---
at System.Net.Http.NSUrlSessionHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x001d4] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.20.2.2/src/Xamarin.iOS/Foundation/NSUrlSessionHandler.cs:527
at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x0017e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:506
at App.Services.LoginService.Login (FileOrbis.Models.RequestModels.LoginRequest request) [0x00084] in C:\Users\PcName\Desktop\App\App\Services\LoginService.cs:40
And simulator log file is:
Startup:
arguments: --device=06098E5B-1853-4A83-8434-8071D8973A14 --launchsim=//Users/deytek/Library/Caches/Xamarin/mtbs/builds/App.iOS/b2c75f2acbd4ff91c305dba10ca791b7/bin/iPhoneSimulator/Debug/App.iOS.app -argument=-monodevelop-port -argument=51890 -setenv=__XAMARIN_DEBUG_PORT__=51890 --sdkroot=/Applications/Xcode.app -h=192.168.1.7 -ssh=deytek --launched-by=devenv-16.0
version: 16.7.0.0 (54a29526ef6f853bdd37adbcc3791ce90ca82735)
Connecting to existing client
Exit:
Exit Code: 0
I encounter with this error when I use Background Session Configuration. If I use normal HttpClient object (without Background Session Configuration), it works
NOTE: I also tried iPhone 5s iOS 12.4.8 and iPad Pro (3rd Generation) iOS 13.6.1 It works these devices. But it doesn't work on iPhone 7 Plus 13.6
Related
I would like to do some operation which doesn't depend on API response and at the same time I want API to finish its process.
But in my case, API doesn't receive request when postasync is executed.
Instead, Web api receive request after await weatherForeCastdata.
I noticed strange behavior today
when I executed endpoint for first time(both the apis), webapi received request after postasync method. From second time, api receives request after executing await weatherForeCastdata.
I launched applictaion from vs code
browser : chrome
Can anyone help me ?
public async Task<IEnumerable<WeatherForecast>> Get()
{
var rng = new Random();
var weatherForeCastdata = new HttpClientCall<WeatherForecast>(_configuration).PostRequest(_configuration["Services:Payperiod"],new WeatherForecast());
Console.WriteLine("apiinvoked");
var data = await weatherForeCastdata;
//var data1 = await data.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(data);
}
public class HttpClientCall<T> where T : class
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
private readonly IConfiguration _configuration;
internal HttpClientCall(IConfiguration configuration)
{
httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
return true;
};
_configuration = configuration;
}
public async Task<string> PostRequest(string apiUrl, T postObject)
{
using (var client = new HttpClient(httpClientHandler))
{
client.DefaultRequestHeaders.Add("ClientId", _configuration["header"]);
Console.WriteLine(apiUrl);
var response = client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter());
var response1=await response;
return await response1.Content.ReadAsStringAsync();
}
}
}
Is there a way of logging the request and response from the client layer(not from controller as we can use middleware to log the same there).
I am looking to eliminate developer code for audit log here (//log request ,//log response and and creating a provider context ) instead move them to a common handler , may be inherit from delegating handler delegating handler and have the Audit log code there.
Any ideas ?
Currently we have audit logging in the client where another service is called but the developer has to do the following :
Client layer code:
{
IRestResponse response = null;
ConnectorHTMLResponse CCMSResponse = null;
request.Validate(request.TemplateName);
var providerContext = _messageTracker.CreateProviderContext(correlationId, "MailTrigger", "GetHTML", OperationProtocols.HTTPS);
//log request
await providerContext.StartAsync(request, param => request.TemplateName);
var bodyJson = ToBodyJson(request, TemplateType.HTML);
try
{
response = await ExecuteAsync(bodyJson, correlationId);
}
catch (Exception ex)
{
await providerContext.RaiseExceptionAsync(ex);
throw;
}
Response = ConstructHTMLDocumentDetails(ValidateResponse(response));
//log response
await providerContext.CompletedAsync(Response);
return Response;
}
//and in the message tracker(Common code )
public static ProviderContext CreateProviderContext(this IMessageTracker messageTracker, string correlationId, string systemId, string operationName, OperationProtocols protocol)
{
var context = new ProviderContext(
messageTracker,
correlationId,
systemId,
operationName,
Assembly.GetCallingAssembly().GetName().Name,
protocol
);
return context;
}
public async Task StartAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
await StartAsync(payload, primaryIdentifierFunc?.Invoke(payload), secondaryIdentifierFunc?.Invoke(payload));
}
public async Task CompletedAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
_source.Payload = payload.AsPayload();
_source.PrimaryIdentifier = primaryIdentifierFunc?.Invoke(payload) ?? _source.PrimaryIdentifier;
_source.SecondaryIdentifier = secondaryIdentifierFunc?.Invoke(payload) ?? _source.SecondaryIdentifier;
await _tracker.TrackProviderResponseAsync(
//track in cloud
);
}``
I been working on a proxy-like controller on ASP.NET, it takes URL from incoming request and uses HttpClient to call the URL, then return the file.
This proxy-like controller work like this:
Works fine when executing one request
Works fine when executing multiple requests,those responses have content-type of "application/json" and "xml"
Request Time-out on 2nd request and Task Cancelled when executing 4 requests, those responses have content-type of "image/png". Like following:
(These 4 request are being sent to controller almost at the same time).
Request 1 - start at 0s , finished at 1s
Request 2 - start at 1s ,Time out and exception throw at 11:00(Time out is 10s), server hangs until timeout
Request 3 - start at 11s , finished at 12s
Request 4 - start at 12s , finsihed at 13s
Please Ignore the POST Situation
Controller
public async Task<FileStreamResult> Proxy(string method, string url, string data = "")
{
myHttpClient client = new myHttpClient();
Tuple<MemoryStream,string> result = await client.Proxy(this.Request, method, url).ConfigureAwait(false);
return File(result.Item1,result.Item2); //file stream and file type
}
HttpClient
public class myHttpClient
{
private static HttpClient _httpClient;
static myHttpClient()
{
if (_httpClient == null) {
_httpClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
_httpClient.Timeout = TimeSpan.FromSeconds(7);
Log ocLog = new Log();
ocLog.Write("init client " + ServicePointManager.DefaultConnectionLimit ,false);
}
}
public async Task<Tuple<MemoryStream, string>> Proxy(HttpRequestBase contextRequest, string method, string url)
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://localhost" + url);
if (method == "GET")
{
request.Method = HttpMethod.Get;
request.Content = null;
}
else if (method == "POST")
{
request.Method = HttpMethod.Post;
}
//copy request header from context.Request
foreach (string headerName in contextRequest.Headers)
{
string[] headerValues = contextRequest.Headers.GetValues(headerName);
if (!request.Headers.TryAddWithoutValidation(headerName, headerValues))
{
request.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
}
}
try
{
using (HttpResponseMessage response = await _httpClient.SendAsync(request).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
var contentBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
MemoryStream ms = new MemoryStream(contentBytes);
return new Tuple<MemoryStream, string>(ms, response.Content.Headers.ContentType.ToString());
}
else
{
return null;
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
What have i tried
Checking ServicePointManager.DefaultConnectionLimit. It is very large.
Use System.net.WebRequest library instead of HttpClient, still the same.
The problem has been solved by setting the "Maximum Process Workers" of the application pool to 3. It did the trick
I've got an asp.net core 2.0 web app (Running using Kestrel) with following controller :
public IActionResult GetUpdateList(string apiCode, int softwareId, [FromBody] List<SoftwareFile> updateFiles)
{
try
{
var stream = SoftwareUpdateFilesHandler.GetUpdateZipFileStream(updateFiles, softwareId);
return File(stream.BaseStream, "application/octet-stream", "UpdateFile");
}
catch (System.Exception ex)
{
return NotFound(ex.ToString());
}
}
and this code on my client :
public async static Task<byte[]> GetUpdateAsync(string apiCode, int softwareId, List<SoftwareFile> updatefiles)
{
try
{
StringContent content = null;
if (updatefiles != null && updatefiles.Count > 0)
{
content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(updatefiles));
content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
}
string address = $"{baseAddress}GetUpdate?softwareId={softwareId}";
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("POST"), address);
request.Content = content;
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var responseContent = await response.Content.ReadAsByteArrayAsync();
return responseContent;
}
catch(Exception ex)
{
return null;
}
}
But when code reaches to httpClient.SendAsync code hangs and after a few seconds client app crashes and I get this exception : The underlying connection was closed: The connection was closed unexpectedly
All of this works fine when I test my code in local but when I publish code and try to call GetUpdateList I get exception.
Odd thing about this is I can't handle exception in my catch block. Somehow catch block doesn't catch this exception and I can see exception in application crash.
I have been stuck all day on a stupid problem with registering a user to my application.
Here is my code once the 'Register' button is clicked:
public ICommand RegisterCommand
{
get
{
return new Command(async() =>
{
var isSuccess = await _apiServices.RegisterAsync(Email, Password, ConfirmPassword);
if (isSuccess){
Message = "Registered Successfully";
}
else
{
Message = "Retry later";
}
});
}
}
Api services Register Async method:
public async Task<bool> RegisterAsync(string email, string password, string confirmPassword)
{
try
{
System.Diagnostics.Debug.WriteLine("Email: "+email);
var client = new HttpClient();
var model = new RegisterBindingModel
{
Email = email,
Password = password,
ConfirmPassword = confirmPassword
};
var json = JsonConvert.SerializeObject(model);
HttpContent content = new StringContent(json);
// content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:63724/api/Account/Register", content);
if (response.IsSuccessStatusCode)
{
return true;
}
return false;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("Error: "+e);
throw;
}
}
}
The Error that I get is:
System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error: ConnectFailure (Connection refused) ---> System.Net.Sockets.SocketException: Connection refused
at System.Net.Sockets.Socket.Connect (System.Net.EndPoint remoteEP) [0x000b6] in <6c708cf596db438ebfc6b7e012659eee>:0
at System.Net.WebConnection.Connect (System.Net.HttpWebRequest request) [0x0016d] in <6c708cf596db438ebfc6b7e012659eee>:0
--- End of inner exception stack trace ---
To me this is very frustrating as I can register a use using Postman with the exact same localhost address. I am following Houssem Dellai's Xamarin.Forms mvc web api tutorials which can be found here
I had an issue with httpclient during the development of my app. I believe there was an issue with the cross-platform implementation of the httpclient class. iOS didn't know how to handle it.
Instead I implemented a very simple httpclient library called flurl: http://tmenier.github.io/Flurl/
First, you will need to install flurl in all project directories (iOS, Android, and the PCL) then the implementation is very simple.
using Flurl;
using Flurl.Http;
public async Task<User> CreateUserAsync(RegisterUserModel userModel)
{
string url = "your/backend/here";
//resp is a user object received and automatically converted into a c# object through the use of .ReceiveJson<typeofobject>();
var resp = await (url).PostJsonAsync(userModel)
.ReceiveJson<User>();
if (resp.LoginSession != null)
{
//Raise my registered event to let other classes know to proceed
OnUserRegistered(resp);
}
return resp;
}
As you can see it makes httpclient implementation very simple. Hopefully this helps.