преобразовать службу WCF в приложение RESTful? - PullRequest
11 голосов
/ 24 марта 2012

Эй, я никуда не денусь, превратив wcf в спокойный сервис.Поэтому мне было интересно, может ли кто-нибудь взять базовый код при запуске приложения службы WCF здесь:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract]
    public interface IService1
    {

        [OperationContract]
        string GetData(int value);

        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);

        // TODO: Add your service operations here
    }


    // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }
}

И службы:

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}

Все, что я сделал, это запустил этоПриложение-служба WCF, открывшее еще один элемент VS2010 с базовой формой, в которой есть кнопка с текстовым полем и метка, и скопировано расположение службы приложения-службы в другом решении, поэтому при вводе номера я получаю ответ от службы.

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
        public ServiceReference1.Service1Client testClient = new ServiceReference1.Service1Client();
        private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = testClient.GetData(Convert.ToInt32(textBox1.Text));
        }
    }
}

Действительно быстро и грязно, но служит своей цели.

Теперь, если кто-нибудь может помочь с кодом, как вы превращаете его в спокойный сервис?

Конечная часть моего файла конфигурации:

 <system.serviceModel>
    <services>
      <service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="wsHttpBinding" contract="WcfService1.IService1">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WcfService1.Service1Behavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="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="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

ИЗМЕНИТЬ обновление с помощью кода конфигурации Джастина:

Поэтому, когда я прикасаюсь к файлу конфигурации, моя обычная ошибка такова: Сбойдобавить услугу.Метаданные службы могут быть недоступны.Убедитесь, что ваш сервис работает и предоставляет метаданные

<code>Error: Cannot obtain Metadata from http://localhost:26535/Service1.svc If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address.  For help enabling metadata publishing, please refer to the MSDN documentation at http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange Error    URI: http://localhost:26535/Service1.svc    Metadata contains a reference that cannot be resolved: 'http://localhost:26535/Service1.svc'.    The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.HTTP GET Error    URI: http://localhost:26535/Service1.svc    There was an error downloading 'http://localhost:26535/Service1.svc'.    The request failed with the error message:--<html>    <head>        <title>Operation 'GetData' in contract 'IService1' has a path variable named 'value' which does not have type 'string'. ÿVariables for UriTemplate path segments must have type 'string'.</title>        <style>         body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}          p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}         b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}         H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }         H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }         pre {font-family:"Lucida Console";font-size: .9em}         .marker {font-weight: bold; color: black;text-decoration: none;}         .version {color: gray;}         .error {margin-bottom: 10px;}         .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }        </style>    </head>    <body bgcolor="white">            <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>            <h2> <i>Operation 'GetData' in contract 'IService1' has a path variable named 'value' which does not have type 'string'. ÿVariables for UriTemplate path segments must have type 'string'.</i> </h2></span>            <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">            <b> Description: </b>An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.            <br><br>            <b> Exception Details: </b>System.InvalidOperationException: Operation 'GetData' in contract 'IService1' has a path variable named 'value' which does not have type 'string'. ÿVariables for UriTemplate path segments must have type 'string'.<br><br>            <b>Source Error:</b> <br><br>            <table width=100% bgcolor="#ffffcc">               <tr>                  <td>                      <code>An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.</code>                  </td>               </tr>            </table>            <br>            <b>Stack Trace:</b> <br><br>            <table width=100% bgcolor="#ffffcc">               <tr>                  <td>                      <code><pre>[InvalidOperationException: Operation 'GetData' in contract 'IService1' has a path variable named 'value' which does not have type 'string'.  Variables for UriTemplate path segments must have type 'string'.]   System.ServiceModel.Dispatcher.UriTemplateClientFormatter.Populate(Dictionary`2& pathMapping, Dictionary`2& queryMapping, Int32& totalNumUTVars, UriTemplate& uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, String contractName) +726   System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter..ctor(OperationDescription operationDescription, IDispatchMessageFormatter inner, QueryStringConverter qsc, String contractName, Uri baseAddress) +94   System.ServiceModel.Description.WebHttpBehavior.GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) +137   System.ServiceModel.Description.WebHttpBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) +659   System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) +3864   System.ServiceModel.ServiceHostBase.InitializeRuntime() +37   System.ServiceModel.ServiceHostBase.OnBeginOpen() +27   System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +49   System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +261   System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +121   System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +479[ServiceActivationException: The service '/Service1.svc' cannot be activated due to an exception during compilation.  The exception message is: Operation 'GetData' in contract 'IService1' has a path variable named 'value' which does not have type 'string'.  Variables for UriTemplate path segments must have type 'string'..]   System.ServiceModel.AsyncResult.End(IAsyncResult result) +11655726   System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +194   System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +176   System.ServiceModel.Activation.HttpModule.ProcessRequest(Object sender, EventArgs e) +275   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Информация о версии: ÿ Microsoft .NET Framework Версия: 2.0.50727.5448;ASP.NET версия: 2.0.50727.5456 -.

Ответы [ 4 ]

8 голосов
/ 24 марта 2012

Как вы уже упоминали, это новый проект, так что, возможно, перенаправление может помочь сделать этот процесс немного более простым?

Вот статья MSDN о том, что вы спрашиваете ,

Тем не менее, я особенно рекомендую взглянуть на ServiceStack , чтобы создать службу RESTful, поскольку она делает процесс чрезвычайно простым.WCF определенно не предоставляет легких средств для достижения этой цели.Они усложняют это в этом случае IMO.

Это было бы то место, где я бы использовал это вместо этого, если это действительно начало проекта

Более прямой ответ приходит от Эта статья немного старше, номожет помочь как понять REST, так и реализовать его в WCF .И это указать ваш GET / POST / PUT / DELETE в веб-атрибуте [Type]

[WebGet(UriTemplate = @"Data?value={value}")]
[OperationContract]
string GetData(int value);

Кроме того, вам нужно будет сделать это в .config вашего приложения (опять же для более старых, Статья MSDN Сконнарда )

<configuration>
   <system.serviceModel>
     <services>
        <service name="Service1">
            <endpoint binding="webHttpBinding" contract="Service1"
                      behaviorConfiguration="webHttp"/>
        </service>
     </services>
     <behaviors>
        <endpointBehaviors>
            <behavior name="webHttp">
                <webHttp/>
            </behavior>
        </endpointBehaviors>
     </behaviors>
  </system.serviceModel>
<configuration>

, которая будет переводиться в вашу конфигурацию как:

<system.serviceModel>
<services>
  <service name="WcfService1.Service1"
        behaviorConfiguration="WcfService1.Service1Behavior">
    <!-- Service Endpoints -->
    <endpoint address="" binding="webHttpBinding" contract="WcfService1.IService1"
        behaviorConfiguration="webHttp">
      <!-- 
          Upon deployment, the following identity element should be removed 
          or replaced to reflect the identity under which the deployed service runs.  
          If removed, WCF will infer an appropriate identity automatically.
      -->
      <identity>
        <dns value="localhost"/>
      </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="WcfService1.Service1Behavior">
      <!-- To avoid disclosing metadata information, set the value below to false 
           and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpGetEnabled="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="false"/>
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
      <behavior name="webHttp">
           <webHttp/>
      </behavior>
  </endpointBehaviors>
</behaviors>

Затем вам, вероятно, потребуется добавить адресприписать конечную точку, чтобы он знал, где искать.

3 голосов
/ 07 мая 2012

При настройке простой службы WCF REST в прошлом у меня была ошибка, аналогичная той, которая у вас была.В статье, упомянутой Джастином: Руководство по проектированию и созданию веб-служб RESTful с WCF 3.5 (поиск - Определение интерфейса HTTP: [WebGet] )

Вы заметите, что все методы Get принимают строки.

У вас возникает ошибка, когда вы пытаетесь преобразовать один из примеров проектов WCF в RESTful.Чтобы исправить это, просто измените сигнатуру метода и интерфейса, чтобы принимать строку , в отличие от int , на что жалуется внутреннее исключение:

Операция 'GetData' в контракте 'IService1' имеет переменную пути с именем 'value', которая не имеет тип 'string'.Переменные для сегментов пути UriTemplate должны иметь тип 'string'

original:

public string GetData(int value)

updated:

public string GetData(string value)

Вот простой раздел .config изУ меня есть пример проекта, который я знаю, работает:

<system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="Service1Behavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="Wcf.Service1">
        <endpoint address=""
                behaviorConfiguration="Service1Behavior"
                binding="webHttpBinding"
                contract="Wcf.IService1"/>
      </service>
    </services>
  </system.serviceModel>
1 голос
/ 24 марта 2012

Используйте атрибут WebGet для операций, которые вы хотите быть доступными в качестве службы RESTful.

Используйте webHttpBinding.

Не забудьте добавить в ваше поведение в конфигурации.достаточно, чтобы начать.

РЕДАКТИРОВАТЬ:

Добавить новую привязку в вашей службе:

<service><endpoint binding="webHttpBinding"... behavior="myBehavior"/> 

и т. д., а затем добавить

<behavior name="myBehavior"><webHttp/></behavior>

как поведение конечной точки.

Атрибут [WebGet] - изучите различные варианты его дальнейшей разработки.

0 голосов
/ 06 января 2015

Это пример проекта от codeproject.com. Необходимо сделать

[WebGet(UriTemplate = "?id={id}")]

вместо

[WebGet(UriTemplate = "{id}")]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...