2015-01-19 2 views
0

я следующий код для службы под названием AuthenticationService:InvalidOperationException при вызове метода службы WCF RESTful с несколькими аргументами?

IAuthenticationService.cs

[ServiceContract] 
public interface IAuthenticationService 
{ 
    [OperationContract] 
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 
    bool Login(string username, string password, string applicationName); 
} 

AuthenticationService.svc.cs

public sealed class AuthenticationService : IAuthenticationService 
{ 
    public bool Login(string username, string password, string applicationName) 
    { 
     // TODO: add the logic to authenticate the user 

     return true; 
    } 
} 

Web.config

<system.serviceModel> 
    <!-- START: to return JSON --> 
    <services> 
     <service name="ImageReviewPoc.Service.AuthenticationService"> 
      <endpoint contract="ImageReviewPoc.Service.Contracts.IAuthenticationService" binding="webHttpBinding" behaviorConfiguration="jsonBehavior"/> 
     </service> 
    </services> 
    <!-- END: to return JSON --> 
    <behaviors> 
     <!-- START: to return JSON --> 
     <endpointBehaviors> 
      <behavior name="jsonBehavior"> 
       <webHttp/> 
      </behavior> 
     </endpointBehaviors> 
     <!-- END: to return JSON --> 
     <serviceBehaviors> 
      <behavior> 
       <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/> 
       <serviceDebug includeExceptionDetailInFaults="false"/> 
      </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <protocolMapping> 
     <add scheme="http" binding="webHttpBinding"/> 
    </protocolMapping> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/> 
</system.serviceModel> 

и у меня есть следующее консольное приложение, использующее услугу:

Program.cs

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     Login().Wait(); 
    } 

    private static async Task Login() 
    { 
     var client = new AuthenticationServiceClient(); 
     var ok = await client.LoginAsync("User", "Password!", "Console Application"); 
     client.Close(); 

     Console.WriteLine(ok); 
    } 
} 

App.config

<system.serviceModel> 
    <client> 
     <endpoint address="http://localhost:62085/AuthenticationService.svc/" 
     binding="webHttpBinding" 
     contract="AuthenticationServiceReference.IAuthenticationService" 
     kind="webHttpEndpoint" /> 
    </client> 
</system.serviceModel> 

я использовал Postman для тестирования служба, отправляющая следующий JSON данные, и она работала:

{"username": "reviewer", "password": "456", "applicationName": "123"} 

Однако, когда я тестировал услугу с помощью консольного приложения я получил

System.InvalidOperationException: Операция «Вход» договора «IAuthenticationService» задает несколько параметров запроса тела для сериализации без каких-либо элементов оболочки. Не более одного параметра тела можно сериализовать без элементов обертки. Удалите дополнительные параметры тела или установите свойство BodyStyle в атрибуте WebGetAttribute/WebInvokeAttribute для обертывания.

Как вы можете видеть из IAuthenticationService.cs кода, я уже установлен BodyStyle в Wrapped. Может ли кто-нибудь привести меня к тому, что я делаю неправильно здесь?

Обратите внимание на следующее:

  • Я уже искать Stackoverflow и Интернет для решения. Почти все решения касаются настройки BodyStyle. Возможно, это помогло другим, но не очень помогло мне.
  • Я пробовал оба Login и LoginAsync; это тот же результат
  • Я ссылался на услугу, добавив ее через «Добавить служебную ссылку» в Visual Studio 2013 (окончательная версия, если вам интересно)
  • Я знаю, что могу позвонить услуге, используя другие способы; например, HttpClient, но то, что я хочу знать, является причиной того, что автогенерированный клиент не работает.
+0

Вы уже обновили свою служебную ссылку на стороне клиента после изменения в 'BodyStyle'? – khlr

+0

Я думаю, запрос json, который идет, не является правильным. вы можете проверить с помощью скрипача и посмотреть, что будет делать запрос. может быть запрос плохо сформирован ... – Saravanan

+0

@khlr, да, я изменил сервис и обновил ссылку не менее 739 000 раз :) – TheBlueSky

ответ

1

Так вы должны позвонить в свою службу.

public void DoLogin() 
    { 
     string uri = "http://localhost:62085/AuthenticationService.svc/Login"; 
     HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri); 

     request.Method = "POST"; 
     request.ContentType = "text/json"; 

     string data = "{\"username\": \"reviewer\", \"password\": \"456\", \"applicationName\": \"123\"}"; 

     System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); 
     byte[] bytes = encoding.GetBytes(data); 

     request.ContentLength = bytes.Length; 

     using (Stream requestStream = request.GetRequestStream()) 
     { 
      // Send the data. 
      requestStream.Write(bytes, 0, bytes.Length); 
     } 

     using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(x)) 
     { 
      using(var responseStream = response.GetResponseStream()) 
      { 
       using(var reader = new StreamReader(responseStream)) 
       { 
        //Here you will get response 
        string loginResponse = reader.ReadToEnd(); 
       } 

      } 
     } 
    } 
+0

Почему «должно»? Почему я не могу использовать автоматически созданный клиент? Я знаю, что могу успешно использовать сервис из Postman, Fiddler, HttpWebRequest, HttpClient и тысячи других способов. То, о чем я спрашиваю, это «Почему это не работает», а не «Как еще я могу его уничтожить». Во всяком случае, я обновил заметки в конце, чтобы избежать путаницы. – TheBlueSky

+0

К сожалению, вы не можете позвонить в службу REST с помощью автоматически созданного клиента. –

+0

Это основано на опыте или официально документированном ограничении? Если последнее, вы можете направить меня к источнику? – TheBlueSky

0

Добавить в Вход/LoginAsync клиента Методы Контракт

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]. 

Таким образом, ваш контракт клиент будет выглядеть следующим образом:

[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IAuthenticationService")] 
public interface IAuthenticationService { 

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IAuthenticationService/Login", ReplyAction="http://tempuri.org/IAuthenticationService/LoginResponse")] 
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 
    bool Login(string username, string password, string applicationName); 

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IAuthenticationService/Login", ReplyAction="http://tempuri.org/IAuthenticationService/LoginResponse")] 
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 
    System.Threading.Tasks.Task<bool> LoginAsync(string username, string password, string applicationName); 
} 

контракт клиента генерируется автоматически. Поэтому вы должны делать это каждый раз после обновления ссылки на службу. enter link description here

+0

Вы говорите, что мне нужно изменить автогенерированный прокси? Зачем? Это известная проблема/ограничение? Можете ли вы посоветоваться с официальным (или другим надежным) источником, который документирует это? – TheBlueSky

+1

Я не могу найти никакой официальной информации о Restful WCF Proxy. Вы можете прочитать это [link] (http://blogs.msdn.com/b/carlosfigueira/archive/2012/03/26/mixing-add-service-reference-and-wcf-web-http-aka-rest- endpoint-does-not-work.aspx) –

+0

Спасибо. Это обходное решение, которое полезно в случае, если человек отчаянно пытается использовать автогенерированный (я не). Наверное, теперь я убежден, что это ограничение. – TheBlueSky

Смежные вопросы