URL routing in WCF/web service - c#

I'm trying to route the request coming to my WCF to a different web service(3.5framework).. Is this even possible? If yes, please let me know the steps..
I need to Route from http://example.com/OldService.svc/Route to http://Newexample.com/NewService.svc/Request
Below are few of the things i tried..Most of the google search returns the routing for webforms and my requirement is for WEB SERVICES.
I tried with the following in my OldService project customroute handler: added a reference to the routing assembly, configured the web.config, added the below code in Global.asax
protected void Application_Start(object sender, EventArgs e)
{
RouteCollection routes = RouteTable.Routes;
//routes.Add("SOAP",new Route("soap", new WebServiceRouteHandler("~/Services/SoapQuery.asmx")));
routes.Add("SOAP", new Route("SOAP", new WebServiceRouteHandler("http://Newexample.com/NewService.svc/Request")));
}
Added a WebServiceRouteHandler as below
public class WebServiceRouteHandler: IRouteHandler
{
private string _VirtualPath;
public WebServiceRouteHandler(string virtualPath)
{
_VirtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new WebServiceHandlerFactory().GetHandler(HttpContext.Current,
"*",
_VirtualPath,
HttpContext.Current.Server.MapPath(_VirtualPath));
}
}
I m calling the oldservice but I m still not redirected to the new Service. Please let me know if i m missing anything here..Should i add anything in my OldService Route method?

I am not sure exactly what you mean by redirect.
Are you saying you would like the Web Service client to be redirected to a new Web Service end point when i tries to contact the old Web Service?
This is not typically possible as services don't have the concept of "redirection." Not to mention if you even tried to redirect to the new service your endpoint bindings would no longer match and the service wouldn't fire anyway.
Is it possible to have the old service call the new service itself and return the results?
Such as Client -> connects to old service -> old service connects to new service -> old service returns the new service results to the client.
You also need to ensure you are returning the exact schema back to the client as it expects based on the WSDL or you will find problems reading the response.
Not sure if this helps at all...

Related

Adding an Authorization header to a WCF scaffolded SOAP web service

Disclaimer: I have not worked with SOAP web services ever. At all. Not even a little bit. So the concept of channels and WCF scaffolding has got me a bit confused, hence I'm here.
I have been asked to integrate to a SOAP XML web service which uses basic authentication. (e.g. Authorization header, Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx <- which is a Base64 encoded username:password). My project is in .NET Core using C#.
I have used Visual Studio WCF connected service discovery to produce scaffolding code which has served me very well for instantiating the required objects etc, however my issue is I've been asked to use Basic authentication, and I have no idea where to inject this code into the scaffolding that's been produced. I have worked with basic authentication before, so I understand 'how' to do it, for things like REST APIs etc. Just username:password, base64 encode and add to Authorization header. However, I am unsure how to do this for this scaffolded SOAP web service.
The code that i believe can be injected into every request, to add your custom headers, is:
using (OperationContextScope scope = new OperationContextScope(IContextChannel or OperationContext)
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
{
Headers =
{
{ "MyCustomHeader", Environment.UserName },
{ HttpRequestHeader.UserAgent, "My Custom Agent"}
}
};
// perform proxy operations...
}
The OperationContextScope expects either an IContextChannel or OperationContext. I am stuck as to what to add here. If I look at my scaffolded code, I can find the 'client' for the web service, here:
public partial class POSUserInformationManagerV1_2Client : System.ServiceModel.ClientBase<McaEndpointPosUserInformation.POSUserInformationManagerV1_2>, McaEndpointPosUserInformation.POSUserInformationManagerV1_2
And I can find the 'channel' here, but it's just another interface, that doesn't have any contracts specified?
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.2")]
public interface POSUserInformationManagerV1_2Channel : McaEndpointPosUserInformation.POSUserInformationManagerV1_2, System.ServiceModel.IClientChannel
{
}
I looked up ChannelBase, and it seems like it should accept a variety of objects that implement one or another channel interface (including IClientChannel, which the scaffolded POSUserInformationManagerV1_2Channel uses)
protected class ChannelBase<T> : IDisposable, IChannel, ICommunicationObject, IOutputChannel, IRequestChannel, IClientChannel, IContextChannel, IExtensibleObject<IContextChannel> where T : class
{
protected ChannelBase(ClientBase<T> client);
[SecuritySafeCritical]
protected IAsyncResult BeginInvoke(string methodName, object[] args, AsyncCallback callback, object state);
[SecuritySafeCritical]
protected object EndInvoke(string methodName, object[] args, IAsyncResult result);
But I'm still stuck on what I can put into the OperationContextScope to connect it appropriately to the 'channel'. I've tried POSUserInformationManagerV1_2Client and the relevent Channel interface, but neither will convert to an IContextChannel. Does anyone have any ideas/thoughts?
EDIT: Here is where I am trying to inject the code to add the Auth HTTP header:
public System.Threading.Tasks.Task<McaEndpointPosUserInformation.requestUserInformationResponse> requestUserInformationAsync(McaEndpointPosUserInformation.requestUserInformation request)
{
using (OperationContextScope scope = new OperationContextScope(request)
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
{
Headers =
{
{ "MyCustomHeader", Environment.UserName },
{ HttpRequestHeader.UserAgent, "My Custom Agent"}
}
};
// perform proxy operations...
}
return base.Channel.requestUserInformationAsync(request);
}
The issue turned out to be not setting up the transport security to be 'Basic' through the use of:
// Set the binding. Without this, the WCF call will be made as anonymous
var binding = new BasicHttpsBinding();
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

Pass username from client to every WCF Service calls

I'm new to .NET and WCF framework. Currently, I'm building a simple windows application to consume WCF service. The windows client need to pass username parameter to WCF Service to save to database. How can I do that without passing parameter in every service requests ?
One thing to note is this service will call a method in another project to save to database , so actually I want to access the username parameter at this project layer.
Form1.cs
public async void Save()
{
using(var client = new MyChannelFactory<WCFService>("WCFService"))
{
await Task.Run(() => client.Proxy.SaveData());
}
}
WCFService.cs
public void SaveData()
{
var _dataBL = new DataBL();
_dataBL.SaveData();
}
DataBL.cs
public void SaveData();
{
//need to get username from client
string user = GetUserName();
//save to database
}
Something to note:
WCFService.cs and DataBL.cs are in different projects.
I'm not adding Service Reference to client project, instead I'm using a ChannelFactory to create service proxy.
I'm using wsHttpBinding
I'm not using Windows Authentication
This should be a common request but after read many articles from internet, I still couldn't make it work.
Thanks for your help.

HTTP status 404 error while calling WCF service from ASP.NET client after web service migration from ASP.NET to WCF

Currently we have a number of customers which use ASP.NET clients to call our various ASP.NET Web Services. We would like to migrate web services to WCF without touching customers' clients and their proxies. In addition we want to avoid using IIS for hosting services.
I've prepared simple prototype to examine if we are able to achieve such migration. This prototype works fine if only a simple namespace of web service is in use e.g. test.com. But because our ASP.NET services have namespaces like test.com/app1, test.com/app2 etc we need to build WCF services accordingly. Unfortunately when such address is used I get HTTP status 404 error while calling service's method.
After a few days of struggling I'm not sure if such migration is possible without hosting services on IIS (the IIS solution is http://msdn.microsoft.com/en-us/library/aa738697(v=vs.100).aspx).
Please, follow steps which I've done to prepare working prototype (with namespace test.com) and tell me where I'm wrong for extended namespace (like test.com/app).
1.
Initial ASP.NET web service:
[WebService(Namespace = "http://test.com")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class AspNetWebService : WebService
{
private static int _counter = 0;
[WebMethod]
public int Increment()
{
return _counter++;
}
}
2.
Deploy web service on IIS 7.0.
3.
Create simple ASP.NET client and generate proxy to web service (from step 1) by adding web reference in Visual Studio 2012 to asmx file of web service.
4.
Client calls Increment method properly, so I will not change this client to check if I may call WCF service.
5.
Create WCF service.
Contract:
[ServiceContract(Name = "IncrementService", Namespace = "http://test.com")]
public interface IIncrementService
{
[OperationContract(Action = "http://test.com/Increment")]
int Increment();
}
Service:
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class IncrementService : IIncrementService
{
private static int _counter = 0;
public int Increment()
{
return _counter++;
}
}
Self hosted in:
static void Main(string[] args)
{
const string httpAddress = "http://test.com";
var host = new ServiceHost(typeof(IncrementService), new Uri(httpAddress));
try
{
var metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadataBehavior);
var httpBinding = new BasicHttpBinding();
httpBinding.Security.Mode = BasicHttpSecurityMode.None;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
host.AddServiceEndpoint(typeof(IIncrementService), httpBinding, httpAddress);
host.Open();
Console.ReadLine();
host.Close();
}
catch (Exception ex)
{
Console.WriteLine("An exception occurred: {0}", ex.Message);
host.Abort();
}
}
6.
Switch off IIS to be sure that ASP.NET web service doesn't work and start WCF service.
7.
Run the same client as before (without any changes or proxy regenerations) - it calls Increment properly.
So far so good... now I extend namespace.
8.
Add app name to namespace in ASP.NET web service as following:
[WebService(Namespace = "http://test.com/app")]
Switch on IIS again, switch off WCF service, regenerate proxy for client to work with new namespace - it calls Increment method properly again.
Change namespaces in WCF service as following:
Contract:
[ServiceContract(Name = "IncrementService", Namespace = "http://test.com/app")]
public interface IIncrementService
{
[OperationContract(Action = "http://test.com/app/Increment")]
int Increment();
}
In host application change httpAddress:
const string httpAddress = "http://test.com/app";
Switch off IIS, start WCF.
Try to call Increment method from the same client - exception occurs: The request failed with HTTP status 404: Not Found.
If I do not change httpAddress in step 10, then all works fine, but in such case I can host only one service on test.com domain. So this solution is not sufficient for us.
Finally I found the solution.
The httpAddress in step 10 should be set the same as URL of ASP.NET Web Service taken from ASP.NET client generated in step 3.
In my case, the URL of Web Service is (you may find it in client's app.config):
http://192.168.1.2/WS_simple/AspNetWebService.asmx
where WS_simple is ASP.NET application name hosted in IIS (created in step 2)
So I changed httpAddress to:
http://localhost/WS_simple/AspNetWebService.asmx
Maybe it's not nice address of WCF service but it works. So migration to WCF service without IIS with backward compatibility is done :)

Web Api and external event handling

I've got a windows service, and I want to expose a web api on this service so our users can interact with the service via http.
I understand how to do a self host web api (http://www.asp.net/web-api/overview/hosting-aspnet-web-api/self-host-a-web-api), now my main problem is how to interface my ApiController with the class it will be executed from, so that when an http request is processed, an event is triggered, and something happens outside of the ApiController.
Based on the following example, how can I register my event handler UserRegistered in my service class ?
Web API Controller
public class MyController : ApiController
{
public delegate void OnPostRegister(Profile user);
public event OnPostRegister UserRegistered;
public void PostStream(Profile user)
{
try
{
IProfileBuilder builder = new ProfileBuilder();
builder.RegisterProfile(user);
if (UserRegistered!= null)
{
UserRegistered(user);
}
}
catch (Exception ex)
{
throw new HttpException(500, ex.Message);
}
}
Service Class
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
//initialize web api
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
}
//Load registered profiles
}
protected override void OnStop()
{
}
}
I don't think you can access the instance of the controller to subscribe to an event directly; I think you probably want a loosely coupled event handler; I was going to suggest the EventAggregator but that is part of Prism which is more for WPF although you could extract it. I did find this which looks like it could solve your issue.
http://blogs.taiga.nl/martijn/2011/07/28/loosely-coupled-events-in-net-and-harvesting-open-source-projects/
I'm sorry but you don't understand the concepts that stays behind web apis and cooperation with other services\apps in .NET world. You can not do like this: calling .NET event to trigger external service - it's crazy idea.
You have few choices here:
A) service bus - which pass events from web api to external service to handle it (NServiceBus, RabitMQ);
B) delegate processing to HangeFire;
C) any custom mechanism to inform external system about web api event\change (db triggers or any shitty ideas that will be difficult to maintain in the end like you are trying to do now)
D) you can create library with the logic from your super windows service and create API for it and you can create HTTP client to call call this API from anywhere - even you could refactor your old service.
Http processing has to be fast and simple. Delegation of the complex tasks should go to other services via additional mechanism\technology and not via .NET event. It is against going practices and impractical from many point of views.

Change service url?

In my web application, I have a service reference (not web service reference).
[DebuggerStepThrough]
[GeneratedCode("System.ServiceModel", "4.0.0.0")]
public partial class LoginClient : ClientBase<Login.LoginClient>, Login.LoginSvc
{
public LoginClient() {
EndpointAddress address = new EndpointAddress(ConfigurationManager.AppSettings["login_svc"]);
this.Endpoint.Binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
this.Endpoint.Address = address;
}
}
Basically, I have the "login_svc" in the AppSettings. However, it throws the flowing exception:
I don't want to add the service configuration to the web.config system.servicemodel....instead I just want to use the appsettings for the url. How do I do this?
Rather than trying to do this in the constructor, you should just use one of the overloaded constructors already available to you when you instantiate the proxy in your client application. For example,
MyService.Service1Client proxy = new MyService.Service1Client(
new BasicHttpBinding(),
new EndpointAddress("<YOUR ENDPOINT ADDRESS>"));
Also, it's not recommended to edit the auto-generated code like this because if you update your service reference, then those changes will be lost because the Reference.cs file is regenerated at that time. Of course, you could copy the Reference.cs and make it a file in your project you manage as one of your own. It's not clear if you were doing that or not but just wanted to mention this just in case.

Categories