I've created a local webservice (.asmx), that I want to add to IIS. The service needs to be called from a Send Adapter in Biztalk.
My project in Visual Studio is structured like so:
There's a single .asmx file, that contains a single web method, see code below:
public class LocalWebService : System.Web.Services.WebService
{
private BankConnectClient client;
[WebMethod]
public void TransferPayment()
{
ProcessDirectory("C:\\Test\\BankConnectTestFiles");
}
I'm not very familiar with IIS, so I don't know best approch to add this service to run on my localhost. I tried adding a new website and placed the project folder in C:\inetpub\wwwroot, which I then reference in IIS with the following settings:
But when I browse to the root http://localhost:61406/, I receive an HTTP Error 403.14.
What is the correct approach in deploying an asmx web service to IIS, to then call in Biztalk using either the WCF-Custom or WCF-BasicHttp adapter?
It seems that the problem has been solved. The service URL need the LocalWebService.asmx suffix.
Besides, as far as I know, BasicHttpBinding in the WCF aims to compatible with ASMX web service, why not try to create a WCF service with BasicHttpBinding. And this is also supported by the BizTalk.
I Have made a demo, wish it is useful to you.
VS template.
Add the following code snippets to the default webconfig.
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
<!--add the following line to support http protocol-->
<add binding="basicHttpBinding" scheme="http"/>
</protocolMapping>
Then publish the project to the IIS folder and add the http binding to the IIS site binding module. We might need to enable the WCF support.
Result.
Feel free to let me know if there is anything I can help with.
Related
I have a simple WCF SOAP service hosted at company's Data Center, having just one Method that receives a Guid (for identifying the request), and returns nothing (void). We use this method as a signal for the service to load its configuration from Database. The service is a self-hosted WCF using SOAP and it is running as a WindowsService.
This service is called from a ASP.NET Web Application, when the user changes configurations related to him. The database is updated, and the service is called to load its updated settings.
The problem is, this service suddenly can't be accessed anymore from the application in production. I get a 404 error, BUT when opening WSDL from browser inside the application's machine, it loads normally.
Trying to reproduce the problem, I created Console Application just to call it from my development machine and it works. BUT, if I try to call it from a new Web Application, another problem happens: "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond".
Also, if I make a call using SoapUI or Visual Studio's WCF Test Client, I can connect without any problem.
For all tests, I used the same approach to made the client: Create a Service Reference (Visual Studio generated code tool). The code used to call the service is something like that:
using (var client = new NotificatorClient())
{
var request = new NotifyRequest { RequestId = Guid.NewGuid() };
client.Notify(request); // Works from any project that is not a WebApplication
Label1.Text = "Notificated!";
}
For all clients, my system.ServiceModel section of Web.config is the same (working on anything that is not a WebApplication, as I said):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_INotificator">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://myservice.com"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_INotificator"
contract="MyAppNotificatorSvc.INotificator" name="BasicHttpBinding_INotificator" />
</client>
</system.serviceModel>
Also, here's my service contracts, if it helps to understand better any problem that could be happening.
[ServiceContract]
public interface INotificator
{
[OperationContract]
void Notify(NotifyRequest request);
}
[DataContract]
public class NotifyRequest
{
[DataMember(Name = "RequestId", Order = 0, IsRequired = true)]
public Guid RequestId { get; set; }
}
We are using HTTPS with TLS 1.2 as our security protocol over requests, and Itried to change it by calling this code on the WebApplication, but I still have the same problem.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
I know that this may sound like not really relevant information about the problem, but this is as far as I got until now. Does anyone came into this type of problem? Any tip on what could be happening?
I have the following code in my Web.Config in a remoting project which is having a reference to a dll of another class library. The class library has the following class inside it:
public class Generator: MarshalByRefObject, IGenerator
{
}
When I run the remoting project for the configured uri which is exposing the above class, I get the mentioned error in the question header.
The url I run is this:
http://localhost/documentgenerationserver/Generator.rem
<application>
<service>
<wellknown mode="SingleCall" objectUri="Generator.rem" type="ABC.Generator, Generator" />
</service>
<channels>
<channel ref="http" />
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channels>
</application>
Can someone advise me, how to resolve this issue?
EDIT 1: After going through an article on remoting on google, I tried appending ?wsdl to my URL above and it worked. It shows me WSDL file for my remote object. In my Client Project which is an asp.net web application hosted in IIS 7.5, when I instantiate the remote object using following:
Activator.GetObject()
It successfully instantiates, but when I use this proxy of remote to call its method, it does not reach up to that method and returns immediately.
Can some one please help me knowing, how can I at least debug it and see, what's happening. Why it is not reaching to the remote object method?
The message "System.Runtime.Remoting.RemotingException: Requested Service not found" is the expected behavior on the browser for a remoting service.
I have created a WCF web service in C# deployed in a Windows Service EXE which is largely working the way I want. I am using it in a self-hosted manner (not within IIS).
In order to make a WSDL file available to the calling Java webservice, I added ServiceMetadataBehavior to the host creation. i.e:
ServiceHost host = new ServiceHost(typeof(MyService));
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
This all worked fine until I moved my service to another server with a different host name. When I connect to the WSDL (http://prod-server:55000/MyService?wsdl), I see that the host name of the development server is hard coded in the WSDL.
Here is a snippet of the WSDL as seen in a browser:
<wsdl:definitions name="MyService" targetNamespace="http://tempuri.org/">
<wsdl:import namespace="MyProject.ServiceContracts" location="http://dev-server:55000/MyService?wsdl=wsdl0"/>
<wsdl:types/>
I have checked all of the C# code in the project, and the development server name is not hard coded anywhere.
In the App.config file, I have the following defined:
<system.serviceModel>
<services>
<service name="MyService">
<endpoint address="http://localhost:55000/MyService" binding="basicHttpBinding"
bindingConfiguration="" contract="MyProject.ServiceContracts.IMyInterface" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:55000/MyService" />
</baseAddresses>
</host>
</service>
</services>
I would expect that this would result in the localhost machine name being substituted, but it persists as the development box name on which the service was originally created / deployed. Am I mistaken?
I also looked into the possibility of explicitly specifying a path to my WSDL file, but from what I can deduce, this can only be done if hosting the service on IIS.
Lastly and purely out of curiosity, I wonder if an actual WSDL file actually gets created (a physical file on disk I mean) or is it dynamically rendered with each request?
It is created dynamically, not every call IIRC, but on first request to the metadata endpoint. I'm not sure why your seeing your DEV server name on the non-DEV machine, but, because you're specifying localhost only in your endpoint address it's going to resolve DNS using the primary network address for the server. You may want to consider adding the useRequestHeadersForMetadataAddress behavior to your config so that the DNS with which the service is accessed is actually used instead.
With WCF the WSDL is dynamically generated.
I have had this problem a number of times on a WCF 3/3.5 service when I needed to send a WSDL to someone as a file. Typically what I do is save the files (typically there are 3, a wsdl for the service, an xsd for your types, and an xsd for the .net types, but your mileage may vary), then manually update the wsdl imports to reference the other two files relative to the wsdl file, then send all three files.
The wsdl:service , wsdl:port, and soap:address will still reference the dev server, but most ws client libraries account for this and allow the developer to configure the endpoint.
I've created a WCF service library with a simple 'hello world' test service and a properly configured App.config file, so that when I start my client application the WCF service is started via Visual Studio's built-in host. The service as it stands is working fine with the external configuration in my client.
I need to run some initialisation code to set up DI, data access, logging etc. I've written a console host that can do that, and the service itself will eventually be deployed as a Windows service, but I want to use the built-in host so that I don't have to manually restart the service during development.
Is there some way I can hook some code in the library to be called on startup?
I found another question about using a custom ServiceHostFactory to perform bootstrapping, which is set up via a *.svc file. *.svc files are part of a WCF Service Application, and can't be used directly by a WCF Service Library. I want to stick to using a service library for some flexibility with my implementation of the services and the eventual production hosting, but using a WCF Service Application would get the job done in a way that suits development (and would probably be easy enough to drop in to IIS for production hosting with an alternate set of configuration). So I figured that I just needed to create a WCF Service Application that acts as a host for the services in the service library, and performs the required initialisation.
I first created a new WCF Service Application, dropped the default service files created by VS, and added a reference to the existing service library and other dependencies.
In the service app's Web.Config file, under the <configSections>..</configSections> section (which is required to be the first node after the <configuration> tag), I added the <system.serviceModel> section for the service library (this can be extracted from the App.config file in the service library and edited to suit).
For example:
<system.serviceModel>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="BelfryImages.QueryService.Implementation.HelloWorld">
<endpoint address="HelloWorld" binding="wsHttpBinding" name="HelloWorld" contract="BelfryImages.QueryService.Contracts.IHelloWorld" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
</bindings>
</system.serviceModel>
Note that the BelfryImages.QueryService.Implementation.HelloWorld is the FQN of the service implementation (implementing class) of the service contract (interface) BelfryImages.QueryService.Contracts.IHelloWorld. I actually have these in two separate assemblies; BelfryImages.QueryService.Contracts.dll is the WCF Service Library.
I added a new blank .svc file to the service application, named to match the endpoint, such as HelloWorld.svc. Usually, adding a WCF Service to a WCF service application results in a .svc file and a .cs codebehind file, which contains the implementation of the service. I manually added just the .svc file with no codebehind and pointed it to the service library implementation:
<%# ServiceHost Language="C#" Debug="true" Service="BelfryImages.QueryService.Implementation.HelloWorld" %>
As it stood this was usable as a drop-in replacement for the VS-hosted service library, after first changing the client's service URL from http://localhost:XXXXX/QueryService/HelloWorld to http://localhost:XXXXX/HelloWorld.svc/HelloWorld.
I then created the ServiceHostFactory to perform initialisation for the service. I found an MSDN article (Hosting and Consuming WCF Services) that explains how to do this (Listings 5-6 and 5-7) - for my purposes I just added a simpler, general purpose CustomHostFactory class:
public class CustomHostFactory
: ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
// perform initialisation:
...
var serviceHost = base.CreateServiceHost(serviceType, baseAddresses);
return serviceHost;
}
}
This is then bound to the service within the .svc file by adding a Factory attribute:
<%# ServiceHost Language="C#" Debug="true" Service="BelfryImages.QueryService.Implementation.HelloWorld" Factory="BelfryImages.WcfService.CustomHostFactory" %>
The initialisation at the top of the CreateServiceHost() override is performed before the service is created as usual. This only seems to happen once per service, rather than once per call to the service, but there will be overhead for multiple services. That could probably made a one-off by using a static flag or similar. For the moment it seems to work fine.
Would it work to have the initialization code in the static constructor of the class that implements your service?
public class WCFService : IWCFService
{
static WCFService()
{
// do initializing here
}
}
This code would execute as the first call is made to the service.
I have 3 web services, all located on the same server.
My Client calls Service A, which impersonates the client to call Service B, and all is well.
Now, I want to impersonate the caller of service B (which is my username) to call Service C. When I use the same technique as before (AllowedImpersonationLevel = Impersonate, user.Impersonate()), The user doesnt get passed to service C. Instead, Service C sees the user as the user I am running it under in IIS (which is a UPN, not the standard NETWORK SERVICE account).
Is there anything special I need to do to get this working? Is this a delegation issue? (I thought it would not be delegation because they are all on the same server)
Thanks SO!
You require delegation in this scenario. The configuration you require is ImpersonationLevel.Delegation (set in config or code). Have a look at the WCF Security Guidance on codeplex it is a very good source. Be careful as achieving delegation, particularly in a production environment, requirements more than simply selecting the correct option in the config file. You need to ensure that the application you connect to, e.g. SQL server, are configured for delegation, and that certain infrastructure requirements are met within active directory and the like, such as service principal names (SPN).
You can try turning on ASP.Net Compatibility on Service C
In Web.cofig
<system.web>
<identity impersonate="true"/>
<authentication mode="Windows"/>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
In your service class
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
public string ExecuteRequest(string xmlRequest)
{
IRequestManager requestManager = new RequestManager();
return requestManager.ProcessRequest(xmlRequest);
}
}
I would have expected to have to use delegation since you are crossing process boundaries twice. Have you tried TokenImpersonationLevel.Delegation?