Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin & IIS LocalHost WebApi gives error - System.Net.Http.HttpRequestException: 'Network subsystem is down'

I have 2 Applications here. One is a Xamarin Application and the other is a Asp.Net MVC Application which has the WebApi to connect to a SqlServer Database "where the database is on a Hosted Website of like some IP Address xxx.xxx.xxx.xxx" .

I run the MVC application Locally to IIS, and then Once the Website is Up in IIS, I run the Xamarin Application from the second instance of Visual Studio.

Firstly I don't think the MVC WebApi gets called, because I tried putting Breakpoints there and it does not come there. I don't know why. Because I had done the same thing with a Winforms and MVC application (WebApi), then at least the WebApi was getting called which I saw with breakpoints in the MVC application.

Now in Xamarin I am getting this error

System.Net.Http.HttpRequestException: 'Network subsystem is down'

in the line

var responseTask = await client.GetAsync("ConnectToAscDatabase");

I have this code in Xamarin Application's MainPage.cs

        private async void Button_OnClicked(object sender, EventArgs e)
        {
            HttpClientHandler handler = new HttpClientHandler();
            handler.UseDefaultCredentials = true;
            using (var client = new HttpClient(handler))
            {
                client.BaseAddress = new Uri("http://localhost:49252/api/");

                // !!! This Line gives the error System.Net.Http.HttpRequestException: 'Network subsystem is down'
                var responseTask = await client.GetAsync("ConnectToAscDatabase");

                var readTask = responseTask.Content.ReadAsAsync<Microsoft.Data.SqlClient.SqlConnection>();
                SqlConnection con = readTask.Result;
                var staty = con.State;
            }
        }

and this is a WebApi

        public class ConnectToAscDatabaseController : ApiController
        {
            public async Task<Microsoft.Data.SqlClient.SqlConnection> Get()
            {
                string SqlServer_ServerPath_Server = "SomeServer";
                string SqlServer_Database_Server = "SomeDatabase";
                string SqlServer_User_Server = "SomeUser";
                string SqlServer_Password_Server = "SomePassword";
                int timeOutInSeconds = 15;
                string connectionString = "data source=" + SqlServer_ServerPath_Server + ";" + "initial catalog=" + SqlServer_Database_Server + ";" + "user id=" + SqlServer_User_Server + ";" + "Password=" + SqlServer_Password_Server + ";" + "Trusted_Connection=false" + ";" + "Connection Timeout=" + timeOutInSeconds + ";" + "Persist Security Info=False";
                SqlConnection con = new SqlConnection(connectionString);
                con.Open();
                return con;
            }
        }
like image 240
BeeGees Avatar asked Dec 14 '19 11:12

BeeGees


Video Answer


1 Answers

From this documentation, Android's Network Address Space is responsible as there is no direct connection from the emulator to your local machine, except with the IP Address "10.0.2.2". You can attach your port number to target whatever services you are trying to reach.

It is also worth noting that for service running with self-signed certificates, you still may not get a connection because of trust issues as Android and IOS will not honor connections to services running with self-signed certificates. Read more from here.

I use the following codes to resolve those issues (On Android):

Shared Project (Add the following interface):

using System.Net.Http;

namespace DemoApp.Services
{
    public interface IHttpClientHandlerService
    {
        HttpClientHandler GetInsecureHandler();
    }
}

Android Project (Add the following class):

using DemoApp.Droid.Services;
using DemoApp.Services;
using System.Net.Http;
using Xamarin.Forms;

[assembly: Dependency(typeof(HttpClientHandlerService))]
namespace DemoApp.Droid.Services
{
    public class HttpClientHandlerService : IHttpClientHandlerService
    {
        public HttpClientHandler GetInsecureHandler()
        {
            HttpClientHandler handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
                {
                    if (cert.Issuer.Equals("CN=localhost"))
                        return true;
                    return errors == System.Net.Security.SslPolicyErrors.None;
                }
            };
            return handler;
        }
    }
}

Now from within your codes you can create a new HttpClient using the following:

#if DEBUG
    HttpClientHandler insecureHandler = DependencyService.Get<IHttpClientHandlerService>().GetInsecureHandler();
    HttpClient client = new HttpClient(insecureHandler);
#else
    HttpClient client = new HttpClient();
#endif

Note: There are better ways of creating an HttpClient (like using dependency injection and IHttpClientFactory) and it is up to the reader to use what suites them best.

For the BaseAddress of HttpClient, you can use the following code as well:

public static string BaseAddress =
    DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:5000";
public static string TodoItemsUrl = $"{BaseAddress}/api/todoitems/";

where port 5000 may be the port to reach your service and todoitems may be the api url to invoke.

like image 96
Wizzyno Avatar answered Oct 20 '22 08:10

Wizzyno