I am using a class in a C# ASP.NET project to allow a script written in some random scripting language to expose webservice methods dynamically - in other words, the script should be able to expose a method of any name with any signature (as long as it's valid, anyway) to the outside world through this SOAP interface (able to add and remove them at will, without needing a hard code change), and as such I need to be able to create a webservice class in C# while being able to dynamically add and remove methods at runtime.
Now, the best plan I've been able to come up with so far is (runtime) generating C# code to represent the webservice, using System.Reflection.Emit to compile it and then loading the assembly at runtime - all whenever the script adds or removes a method to/from the service (should not happen very often, mind).
Does anyone have a better idea than this?
You can modify WSDL by using SoapExtensionReflector class. From Kirk Evans Blog:
The SoapExtensionReflector is called when your type is being reflected over to provide the WSDL definition for your service. You can leverage this type to intercept the reflection call and modify the WSDL output.
The following example removes the first method out of 2 web service methods:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public int Multiply(int a, int b)
{
return a * b;
}
}
Create a class inherited from SoapExtensionReflector:
namespace TestWebservice
{
public class MyReflector : SoapExtensionReflector
{
public override void ReflectMethod()
{
//no-op
}
public override void ReflectDescription()
{
ServiceDescription description = ReflectionContext.ServiceDescription;
if (description.PortTypes[0].Operations.Count == 2)
description.PortTypes[0].Operations.RemoveAt(0);
if (description.Messages.Count == 4)
{
description.Messages.RemoveAt(0);
description.Messages.RemoveAt(0);
}
foreach (Binding binding in description.Bindings)
{
if (binding.Operations.Count == 2)
binding.Operations.RemoveAt(0);
}
if (description.Types.Schemas[0].Items.Count == 4)
{
description.Types.Schemas[0].Items.RemoveAt(0);
description.Types.Schemas[0].Items.RemoveAt(0);
}
}
}
}
Add this to configuration/system.web section in web.config:
<webServices>
<soapExtensionReflectorTypes>
<add type="TestWebservice.MyReflector, TestWebservice" />
</soapExtensionReflectorTypes>
</webServices>
This should give you a starting point to dynamically removing methods from WSDL document. You would also need to throw NotImplementedException from web method if it is disabled.
Finally, you need to disable web service documentation produced by invoking .asmx endpoint without ?WSDL parameter. Set href attribute of wsdlHelpGenerator element to some URL. You can use DefaultWsdlHelpGenerator.aspx as a starting point for your own documentation handler. See question on web service documentation in XML Files, August 2002.
XMLRPC is fairly dead, isn't it?
SOAP implies a WSDL. How do you generate the WSDL dynamically?
You should look into using WCF. I expect you'll be able to take control of the process of generating the WSDL (and other metadata), yet you should also be able to take control of the processing of incoming messages. In particular, you will be able to examine the incoming messages to determine which script to run, what parameters to pass, etc.
You could create a WCF service with an input and output type of xs:any and handle the incoming request as a raw Message. That would allow you to accept any type of data and return any type of data. You would not use data contracts or static types, just a Message in and a Message out.
The problem with this approach is that generating a proxy from the WSDL really does nothing to help the consumer other than provide a wrapper to call the method. Providing data that is acceptable to the method would require hand-rolling data types, etc which is not that hard, it is just not as intuitive as a hard, typed contract.
Does it have to be a SOAP interface? That sounds like it might be more suitable to a route/REST/etc based API. You could do something in ASP.NET MVC (with a custom IController.Execute method that resolves the action to the method) pretty easily (in fact, I'm working on something very similar for some of my own code at the moment).
For example, you might have routes:
http://myserver/myservice/mymethod
that accepts (either in the body or args) the payload (parameters), and returns the result in the response. In non-MVC you should be able to do something similar with a wildcard-mapped generic handler.
Here is a suggestion:
Use WCF.
Create a WSDL from WCF using
information in the following blog
post:
http://www.pluralsight.com/community/blogs/kirillg/archive/2006/06/18/28380.aspx
You can then publish this information
using MEX.
The services are then open for your
clients to down load the meta data
and call the services.
Related
I need to create a web service to receive a SOAP request that a customer uses to send out information to various systems, all I have is an example xml payload. I have created a test WCF web service using
https://www.c-sharpcorner.com/article/create-simple-wcf-service-and-host-it-on-console-application/
and was going to just create a class along the lines of
public class WebServiceSOAP : IWebServiceSOAP
{
public string SetProductInfo(string strSOAPRequest)
{
// Parse the xml received, validate etc
string response = ParseSOAPData(strSOAPRequest);
// response depends on if data passes validation or not.
return response;
}
}
and host it in a console App. I then came across SoapService Class during my googling but I am not clear on how this fits in with a normal WCF service as the example here
https://msdn.microsoft.com/en-gb/library/ms819935.aspx
seems to use Microsoft.Web.Services2/3 instead of System.ServiceModel does this mean I can use the same method to set up my webservice as the example I have previously followed? Including the App.config. The example seems for System.ServiceModel seems completely different to the setup of the WCF service I followed. I am clearly missing something. I can't inherit from both the interface and SoapService, plus I would have a function with a [OperationContract] and a [SoapMethod] attribute.
It looks like I could receive the SOAPEnvelope and use SoapEnvelope.GetBodyObject to get the body / payload but I am not clear if it returns xml or if the body still has all the < replaced with < etc Can I just take the result of SoapEnvelope.GetBodyObject and parse the xml?
What's my Problem
Object returned from the ASMX service is used in Silverlight application. Class has methods but the result from the ASMX WebMethod does not show methods on the object.
Tell me more
here is my class
public class Dog
{
public string Name{get;set;}
public void Bark();
}
here is the WebMethod
[WebMethod]
public List<Dog> Findlabrador()
{
blah blah blah
return list_of_labrador;
}
the silverlight code
void LabradorFetchCompleted(object sender, LabradorFetchCompletedEventArgs e)
{
var list_of_labrador = e.Result;
foreach(var labradorDog in list_of_labrador)
{
labradorDog.Bark();
//** WTH my labrador can't BARK** Bark method is not shown in intellisense there is compilation error if i explicitly specify
}
}
I am a programmer not a layman
Ok hmm, let me put in your words. Here are steps for you to reproduce the issue
Create a Silverlight Application project ( Let VS create Website to host the application)
Create a Silverlight Class library create the Dog class inside it
Compile the Silverlight Class library to assembly(Dog.dll)
Add reference to Dog.dll silverlight assembly to the silverlight application project
Add a WebService application to the project ( DogService.asmx note the asmx extension)
Add a reference to the Silverlight Dog.dll assembly for the DogService
return hardcoded List<Dog> class from a WebMethod inside it
Add a reference from the Service to Silverlight application, create a instance of proxy client and invoke the method
Watch as your Dog too can't Bark :(
Methods are never serialized. Only data. Your methods, events, indexers, constructors, etc, will never be serialized.
You should not be using ASMX services anyway. Use WCF instead. WCF, among other things, gives you the ability to share datatypes between the client and service. This would allow something like "serializing methods": the same methods could be used both on the client and server.
You are supposed to define all common classes using portable class libraries, http://msdn.microsoft.com/en-us/library/gg597391.aspx
And then when consuming the web service within Silverlight, you should ask the proxy generator to reuse those classes. That makes sure you get all the functions.
Web service definition (WSDL) only takes care of fields/properties. Methods are not transferred over the wire.
I have multiple WCF services that share some data contracts and need to generate client-side code using svcutil.exe. I've run into errors using two most obvious ways to do this and need some help.
But first, here are the services:
[ServiceContract( Namespace = "http://www.me.com/services/" )]
public interface IFooService {
[OperationContract]
Response RunFoo( Request request );
}
[ServiceContract( Namespace = "http://www.me.com/services/" )]
public interface IBarService {
[OperationContract]
Response RunBar( Request request );
}
Response and Request are defined in a separate assembly:
[DataContract( Namespace = "http://www.me.com/shared/" )]
public class Request {
[DataMember]
public int Input { get; set; }
}
[DataContract( Namespace = "http://www.me.com/shared/" )]
public class Response {
[DataMember]
public int Result { get; set; }
}
The services are implemented in some trivial way, compiled, published - let's switch to the client side now.
Including both services on the svcutil command line - like this:
svcutil /o:Client.cs http://hostname.com/FooService.svc http://hostname.com/BarService.svc
will result in numerous error messages about duplicated data types, starting with
Error: There was a validation error on a schema generated during export:
Source:
Line: 1 Column: 9087
Validation Error: The global element 'http://schemas.microsoft.com/2003/10/Serialization/:anyType' has already been declared.
and ending with
Error: There was a validation error on a schema generated during export:
Source:
Line: 1 Column: 12817
Validation Error: The complexType 'http://www.me.com/shared/:Response' has already been declared.
Generating a client-side file separately for each service avoids these errors:
svcutil /o:Foo.cs http://hostname.com/FooService.svc
svcutil /o:Bar.cs http://hostname.com/BarService.svc
But then definitions of shared types (such as Request and Response) will be duplicated in Foo.cs and then in Bar.cs, resulting obviously in compiler errors.
So, what is the conventional way to generate client-side code consuming such services?
Limitations:
cannot ship an assembly containing shared types to the client (so that they could use svcutil.exe's /r option)
cannot use the "Add Service Reference..." command in Visual Studio - need an svcutil command line (or another command-line tool).
Well, basically you can
either put your shared types into a separate assembly that the clients can use when generating the client code (which you already dismiss as impossible)
or then:
you have to generate each proxy for the services separately, and each service will get its own "copy" of the "Request" and "Response" classes
Either you can share the common assembly - or you can't - I don't see any other choice, really.
Since you have rules out a shared DTO assembly (why, btw?), the simplest option in this case looks to be to generate the types in different C# namespaces (i.e. two calls to svcutil), and map the data between the two. Essentially: treat the DTOs from the two services as coincidentally similar.
You can use things like automapper to reduce the work, or you could juts serialize from type A and deserialize into type B (assuming the actual data namespaces etc are identical).
WSCF Blue may get you closer to a solution if you've not found one already.
http://wscfblue.codeplex.com/
It can produce separate files for each type, overwriting on subsequent operations.
When you run the client utility once you will get a XXXXService.cs and a output.config file.
If you observe the XXXXService class you have everything in a file. You can split these as a seperate IXXXService and XXXService file and the datacontracts file.
Then you can run the utility for the second service and add IXXXService1.cs and 1XXXService.cs file and the same datacontracts you can use to share for these 2.
I am not sure if this can answer your question. I had an example which can help you.
You can see some more examples here related to some MVC and WCF stuff.
Please download WSCFblue-v1-Walkthrough zip from the below link, it might help you acheive it.
http://wscfblue.codeplex.com/releases/view/48529
I have created a SoapExtension class to capture the soap request and response from specific web service calls. In order to put this SoapExtension into effect, I have to add an attribute to the method in the generated proxy client.
For example, I've added the AuditSoapCapture attribute to this method:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://blahblah.com/webservices/AddressSearch", RequestNamespace = "http://blahblah.com/webservices/", ResponseNamespace = "http://blahblah.com/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[blahblah.TriadLite.Core.AuditSoapCapture]
public System.Data.DataSet AddressSearch(string HouseNumber, string StreetName, string ZipCode) {
object[] results = this.Invoke("AddressSearch", new object[] {
HouseNumber,
StreetName,
ZipCode});
return ((System.Data.DataSet)(results[0]));
}
I am looking for a way to add this attribute to specific methods without modifying the generated client proxy, as they will get lost when we regenerate. Can I do this in a another partial class or interface or some other way?
Thanks!
Unfortunately, you'll need to modify the proxy code. The other possibilities you mention will not work - a parial class will not overwrite existing functionality, and there is no way that I'm aware of getting an interface to do what you need (compounded by the fact that there is no way to even let the proxy generator know that you intend to implement an interface).
Something that I've done in the past, in a situation where you have access to the source of the webservice, is to write a little app that will parse the code (as text) in the .asmx.cs file of the webservice to extract the names of all the methods that are tagged with [WebMethod]. Then the app "fixes up" the References.cs by inserting appropriate attributes onto the proxied methods, based on some settings file or somesuch. This works well because the naming conventions in the proxy map very neatly to the method names in the original service.
I may just end up injecting my SoapExtension by putting it into the Web.config. This will cause it to be run on every WS call without a client proxy method attribute. Then, I will modify the SoapExtension to look up the called WS method name on a list, and if it is on the list, then do the rest of the SoapExtension logic. I figure the hit on the list in this small volume application isn't going to kill performance.
6 years ago this was posted... So not sure if this will help anyone at this point.
I ran into something similar with a call to an old SOAP web service that had a dynamically generated proxy class that we didn't want to modify as it was auto-generated from the wsdl by the project. In order to solve this problem here is what we did.
The proxy class generated by wsdl.exe is a partial class. We extended this class like so to add a property with the information we wanted to access in the soapextension. You can add as many properties as you want...
partial class mysoapwebservice
{
public string myproperty{ get; set; }
}
in the web.config we registered the soap extension globaly on the project
<webServices>
<soapExtensionTypes>
<add type="MySoapExtension" priority="1" group="Low"/>
</soapExtensionTypes>
</webServices>
In the code were we created the web service object 'mysoapwebservice' we set the value of the property we needed.
In the soapextension you can get a reference to the web service that was called as well as the values. You can also determine the method call.
`
public class MySoapExtension: SoapExtension
{
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
{
// web service client object
var webserviceobject= ((SoapClientMessage)message).Client;
// method from web service that was called
var calledMethod = (SoapClientMessage)message).MethodInfo;
// checked the client type of webserviceobject and
//added method / property specific logic here
}
}
}
// other soap extension code
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class CriptoExtensionAttribute : SoapExtensionAttribute
[CriptoExtension]
public partial class MainService{
Say I have an ASMX web service, MyService. The service has a method, MyMethod. I could execute MyMethod on the server side as follows:
MyService service = new MyService();
service.MyMethod();
I need to do similar, with service and method not known until runtime.
I'm assuming that reflection is the way to go about that. Unfortunately, I'm having a hard time making it work. When I execute this code:
Type.GetType("MyService", true);
It throws this error:
Could not load type 'MyService' from assembly 'App_Web__ktsp_r0, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Any guidance would be appreciated.
I'm not sure if this would be the best way to go about it. The most obvious way to me, would be to make an HTTP Request, and call the webservice using an actual HTTP GET or POST. Using your method, I'm not entirely sure how you'd set up the data you are sending to the web service. I've added some sample code in VB.Net
Dim HTTPRequest As HttpWebRequest
Dim HTTPResponse As HttpWebResponse
Dim ResponseReader As StreamReader
Dim URL AS String
Dim ResponseText As String
URL = "http://www.example.com/MyWebSerivce/MyMethod?arg1=A&arg2=B"
HTTPRequest = HttpWebRequest.Create(URL)
HTTPRequest.Method = "GET"
HTTPResponse = HTTPRequest.GetResponse()
ResponseReader = New StreamReader(HTTPResponse.GetResponseStream())
ResponseText = ResponseReader.ReadToEnd()
// Try this ->
Type t = System.Web.Compilation.BuildManager.GetType("MyServiceClass", true);
object act = Activator.CreateInstance(t);
object o = t.GetMethod("hello").Invoke(act, null);
Although I don't know why Reflection is not working for you there (I assume the compiler might be creating a new class from your [WebService] annotations), here is some advice that might solve your problem:
Keep your WebService simple, shallow, in short: An implementation of the Facade Pattern.
Make your service delegate computation to an implementation class, which should easily be callable through Reflection. This way, your WebService class is just a front for your system - you can even add an email handler, XML-RPC frontend etc., since your logic is not coupled to the WebService, but to an actual business layer object.
Think of WebService classes as UI layer objects in your Architecture.
Here's a quick answer someone can probably expand on.
When you use the WSDL templating app (WSDL.exe) to genereate service wrappers, it builds a class of type SoapHttpClientProtocol. You can do it manually, too:
public class MyService : SoapHttpClientProtocol
{
public MyService(string url)
{
this.Url = url;
// plus set credentials, etc.
}
[SoapDocumentMethod("{service url}", RequestNamespace="{namespace}", ResponseNamespace="{namespace}", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public int MyMethod(string arg1)
{
object[] results = this.Invoke("MyMethod", new object[] { arg1 });
return ((int)(results[0]));
}
}
I haven't tested this code but I imagine it should work stand-alone without having to run the WSDL tool.
The code I've provided is the caller code which hooks up to the web service via a remote call (even if for whatever reason, you don't actually want it to be remote.) The Invoke method takes care of packaging it as a Soap call. #Dave Ward's code is correct if you want to bypass the web service call via HTTP - as long as you are actually able to reference the class. Perhaps the internal type is not "MyService" - you'd have to inspect the control's code to know for sure.
#Kibbee: I need to avoid the HTTP performance hit. It won't be a remote call, so all of that added overhead should be unnecessary.
#Daren: I definitely agree with that design philosophy. The issue here is that I'm not going to be in control of the service or its underlying business logic.
This is for a server control that will need to execute against an arbitrary service/method, orthogonally to how the web service itself is implemented.
Although I cannot tell from your post:
One thing to keep in mind is that if you use reflection, you need to create an instance of the autogenerated webservice class(the one created from your webservice's WSDL). Do not create the class that is responsbile for the server-side of the service.
So if you have a webservice
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class WebService1 : System.Web.Services.WebService
{
...
}
you cannot reference that assembly in your client and do something like:
WebService1 ws = new WebService1 ();
ws.SomeMethod();
#Radu: I'm able to create an instance and call the method exactly like that. For example, if I have this ASMX:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
I'm able to call it from an ASPX page's codebehind like this:
MyService service = new MyService();
Response.Write(service.HelloWorld());
Are you saying that shouldn't work?
I looked back at this question and I think what you're facing is that the ASMX code will be built into a DLL with a random name as part of the dynamic compilation of your site. Your code to look up the type will, by default, only search its own assembly (another App_Code DLL, by the looks of the error you received) and core libraries. You could provide a specific assembly reference "TypeName, AssemblyName" to GetType() but that's not possible in the case of the automatically generated assemblies, which have new names after each recompile.
Solution.... I haven't done this myself before but I believe that you should be able to use something like this:
System.Web.Compilation.BuildManager.GetType("MyService", true)
as the BuildManager is aware of the DLLs it has created and knows where to look.
I guess this really doesn't have to do with Web Services but if it were your own code, Daren's right about Facade patterns.