Search code examples
wcfxamarin.androidsystem.net.webexception

system.net.webexception:error on the remote server: (400) bad request


I've created a wcf webservice that has a login method. this is my code: IService1.cs

[ServiceContract]
    public interface IService1
    {
        [OperationContract]

        [WebInvoke( BodyStyle = WebMessageBodyStyle.Wrapped,ResponseFormat =WebMessageFormat.Xml, Method ="POST")]
        bool LoginUserDetails(string name, string pass);
    }

Service1.svc.cs

public class Service1 : IService1
    {
        public bool LoginUserDetails(string name, string pass)
        {
           
            SqlConnection con = new SqlConnection("server=DESKTOP-CPOJ94O\\MSSQLSERVER1;database=users;integrated security=true");
            con.Open();
            SqlCommand cmd = new SqlCommand("SELECT username, password FROM hjk where CONVERT(VARCHAR, username)=@username and CONVERT(VARCHAR, password)=@password", con);
            cmd.Parameters.AddWithValue("@username", name);
            cmd.Parameters.AddWithValue("@password", pass);
            bool result = false;
            SqlDataReader dr = cmd.ExecuteReader();
            if (dr.HasRows)
            {
                result = true;
                return result;
            }
            else
            {
                result = false;

            }
            return result;
        }
    }

web.config

<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.7.2" />
    <httpRuntime targetFramework="4.7.2"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfService4.Service1" behaviorConfiguration="ServiceBehavior">
        <endpoint address="" binding="webHttpBinding" contract="WcfService4.IService1" behaviorConfiguration="web">
          
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    <endpointBehaviors >
      <behavior name="web">
        <webHttp/>
      </behavior>
    </endpointBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="webHttpBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

then I tried to consume this service in Xamarin.android app. in my main activity I wrote the following:

 EditText editText1 = FindViewById<EditText>(Resource.Id.editText1);
            EditText editText2 = FindViewById<EditText>(Resource.Id.editText2);
            TextView txtview = FindViewById<TextView>(Resource.Id.textView1);
            //System.IO.StreamWriter myWriter = null;
            string txt1 = editText1.Text;
            string txt2 = editText2.Text;
            Button btn = FindViewById<Button>(Resource.Id.button1);
           
            btn.Click += delegate { 
string myRequest = "name=rana&pass=ranahd";
                string myResponse = "";
                string myUrl = "http://192.168.0.104/wcf1/Service1.svc/LoginUserDetails";
                System.IO.StreamWriter myWriter = null;// it will open a http connection with provided url
                System.Net.HttpWebRequest objRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(myUrl);//send data using objxmlhttp object
                objRequest.Method = "PUT";
                //objRequest.ContentLength = TranRequest.Length;
                objRequest.ContentType = "text/xml";//to set content type
                myWriter = new System.IO.StreamWriter(objRequest.GetRequestStream());
                myWriter.Write(myRequest);//send data
                myWriter.Close();//closed the myWriter object
                try
                {
                    System.Net.HttpWebResponse objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();//receive the responce from objxmlhttp object 
                    using (System.IO.StreamReader sr = new System.IO.StreamReader(objResponse.GetResponseStream()))
                    {
                        myResponse = sr.ReadToEnd();
                    }

                    txtview.Text = myResponse;
                }
                catch(Exception exp)
                {
                    System.Net.WebException exception = new System.Net.WebException();
                    Toast.MakeText(this,exception.Message,ToastLength.Long).Show();
                }
            };

        }

I got the exception: system.net.webexception:error on the remote server: (400) bad request. and when I read the message in the toast it said: operation is not valid due to current state of the object. I did my research but I couldn't find what the problem was. what did I do wrong? thanks in advance.


Solution

  • Since the XML format has only one root and the JSON format has only one object, only one parameter can be passed to the method. In fact, if you changed a service contract to something like:

      [OperationContract]
      [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, Method = "POST")]
      bool LoginUserDetails(string name, string pass);
    

    a service would throw a exception:

    enter image description here

    Changing WebMessageBodyStyle to wrapped is not the best way to solve this exception.

    We should encapsulate username and password into a class:

    [DataContract]
    public class User {
        [DataMember]
        public string username { get; set; }
        [DataMember]
        public string pass { get; set; }
    }
    

    This is interface:

     [OperationContract]
     [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, Method = "POST")]
     bool LoginUserDetails(User user);
    

    I suggest you enable the help document in WCF:

    <webHttp helpEnabled="true"/>
    

    enter image description here

    Results

    enter image description here

    enter image description here

    Feel free to let me know if the problem persists.

    UPDATE

    This is my demo:

     [OperationContract]
    
     [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, Method = "POST")]
     bool LoginUserDetails(User user);
    

    This is the interface of the service.

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:8763/TEST/LoginUserDetails");
                request.Method = "POST";
                request.ContentType = "application/json;charset=UTF-8";
                string Json = "{\"pass\":\"123\",\"username\":\"sdd\"}";
                request.ContentLength = Encoding.UTF8.GetByteCount(Json);
                Stream myRequestStream = request.GetRequestStream();
                StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
                myStreamWriter.Write(Json);
                myStreamWriter.Close();
    
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream myResponseStream = response.GetResponseStream();
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
                string retString = myStreamReader.ReadToEnd();
                myStreamReader.Close();
                myResponseStream.Close();
                Console.WriteLine(retString);
                Console.ReadKey();
    

    This is the client's request.

    enter image description here