How to browse application on service fabric? - c#

How do I find out what endpoint I should be requesting in order to trigger GetAccounts?
I've got two applications running on my local cluster:
The fabric/Service is a web api application with the following configuration:
internal sealed class Web : StatelessService
{
public Web(StatelessServiceContext context)
: base(context)
{
}
/// <summary>
/// Optional override to create listeners (like tcp, http) for this service instance.
/// </summary>
/// <returns>The collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp,
serviceContext, ServiceEventSource.Current, "ServiceEndpoint"))
};
}
}
The startup is configured like so:
public static class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public static void ConfigureApp(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.MapHttpAttributeRoutes();
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
appBuilder.UseWebApi(config);
}
}
And finally the service manifest:
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="WebPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="WebType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>removed...........Accounts.Web.exe</Program>
<WorkingFolder>CodePackage</WorkingFolder>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" />
</Endpoints>
</Resources>
</ServiceManifest>
And my controller:
[HttpGet]
[Route("accounts", Name = "GetAccounts")]
public async Task<IHttpActionResult> GetAccounts(){//dostuff}
How do I find out what endpoint I should be requesting in order to trigger GetAccounts?

Service Fabric provides a built-in reverse proxy. It is enabled by default in your local development cluster. A reverse proxy allows you to use dynamic ports (as shown in your .gif). When using a reverse proxy you can call your service with the port number of your reverse proxy (19081 by default). The address format in your use case, a stateless service with a singleton partition, is:
protocol://clusterAddress:reverseProxyPort/applicationName/serviceName
In your example the service would be called with:
http://clusterAddress:19081/Service/Web/api/controller/accounts/GetAccounts

I suppose this Web Api is exposed to the outside world?
The stateless service you are using to host it in has dynamic port enabled.
For external facing services it is best to give it a fixed port.
In the service manifest file you can add the port number in the endpoint definition:
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="80">
See See this link for more info.
Once you have the portnumber you can access the web api at http://localhost:80/api/[controller]/accounts
You can then lookup the actual port number in the explorer, whether you are using dynamic ports or not.
To see the endpoint port number browse to a node beneath your service like this:
(See the endpoint at the right side?)
Note that if the endpoint contains the ip of a specific node, you need the ip or FQDN of the cluster. But for now it seems ok since you are using localhost.

In Service Fabric, a service runs somewhere in a Service Fabric cluster, typically distributed across multiple VMs. It can be moved from one place to another, either by the service owner, or automatically by Service Fabric. Services are not statically tied to a particular machine or address.
A Service Fabric application is generally composed of many different services, where each service performs a specialized task. These services may communicate with each other to form a complete function, such as rendering different parts of a web application. There are also client applications that connect to and communicate with services.
For example, in order to accept external traffic on port 80, the following things must be configured:
Write a service that listens on port 80. Configure port 80 in the service's ServiceManifest.xml and open a listener in the service, for example, a self-hosted web server.
XML
<Resources>
<Endpoints>
<Endpoint Name="WebEndpoint" Protocol="http" Port="80" />
</Endpoints>
</Resources>
C#
class HttpCommunicationListener : ICommunicationListener
{
...
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
EndpointResourceDescription endpoint =
serviceContext.CodePackageActivationContext.GetEndpoint("WebEndpoint");
string uriPrefix = $"{endpoint.Protocol}://+:{endpoint.Port}/myapp/";
this.httpListener = new HttpListener();
this.httpListener.Prefixes.Add(uriPrefix);
this.httpListener.Start();
string publishUri = uriPrefix.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
return Task.FromResult(publishUri);
}
...
}
class WebService : StatelessService
{
...
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] { new ServiceInstanceListener(context => new HttpCommunicationListener(context))};
}
...
}
This document discusses how to set up communication with and between services in Service Fabric:
Connect and communicate with services in Service Fabric

Related

Can httpModule "capture" outgoing http requests from a web API hosted on IIS?

I have created a class library test project that has one class which implements the IhttpModule interface.
I have a demo web api project that is hosted on my local IIS and has an HttpPost method which inside uses a class that makes outgoing Http requests at third parties.
As of now it works like a charm for the incoming requests but i want to catch every request that comes and goes from my API. I do not care about the responses.
I have already checked the requests made at the third parties and they are correct.
API's web.config :
<system.webServer>
<modules>
<add name="MyIISModule" type="IisTestProject.MyIISModule" />
</modules>
</system.webServer>
MyIISModule.cs :
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(OnBeginRequest);
}
private void OnBeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication) sender;
File.AppendAllLines("C:\\requestHeaders.txt", new List<string>() {$"{DateTime.Now.ToOADate()}_{app.Context.Request.ToString()}" });
}
public void Dispose()
{
}
I made this so i can just check whenever a request happens.
Is there any way to do this without changing my API's code and using only my HttpModule?
Also i have found this:
HTTP modules can only see requests going through IIS/ASP.NET pipeline, while such outbound requests go through Windows sockets directly, and not through IIS/ASP.NET pipeline.
Is there any way to circumvent this?

dynamically set the base address of a WCF service in the client

I built a WCF service library and hosted it through a host application. Then I constructed a client application, but it seems that the address of the service host is hard coded in the client program. What if the host changes its address? Is it possible to write the client application so that the address of the host can be entered by the client at run time?
Yes, it's possible, if you write the WCF client proxy by hand, instead of generating it automatically with Visual Studio adding a service reference.
Let's start from this example (https://learn.microsoft.com/it-it/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory), just to understand how ChannelFactory works, and then modify it a little bit, adding the following function.
private ChannelFactory<IMath> _myChannelFactory;
// ...
private IMath GetChannel(string endpointConfigurationName, string endpointAddress)
{
if (_myChannelFactory == null)
{
this.DebugLog("Channel factory is null, creating new one");
if (String.IsNullOrEmpty(endpointAddress))
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName);
}
else
{
_myChannelFactory = new ChannelFactory<IMath>(endpointConfigurationName, new EndpointAddress(endpointAddress));
}
}
return _myChannelFactory.CreateChannel();
}
You can define the default server IP in the client App.config file
<system.serviceModel>
<!-- ... -->
<client>
<endpoint address="net.tcp://192.168.10.55:81/math/" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IMath"
contract="MyNamespace.IMath" name="NetTcpBinding_IMath" />
</client>
</system.serviceModel>
In this way, when GetChannel("NetTcpBinding_IMath", "net.tcp://127.0.0.1:81/math") is called, it picks up the endpoint configuration from App.config file, replacing the default address (192.168.10.55) with the new one (127.0.0.1).
Some more documentation about ChannelFactory: https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.channelfactory-1.createchannel?view=netframework-4.8

Inject Service Reference into .NET with AppSettings.json and Startup.cs

My project is not finding the service reference endpoint in runtime. I believe it's due to incorrect injection in my Startup.cs. I'm new to the appsettings.json and Startup.cs method of configuration but have successfully scoped my class library and Dbcontext in the Startup.cs.
Note, if it makes a difference, this VS solution contains a class library and a .NET/angular2 web project. The call to the Service is initiated from angular website to the Web API, which calls methods on the class library where actual processing occurs.
The service reference "CybersourceTrxnProcessor" shows up in my class library project (see image) and ITransactionProcessor is exposed and accessible (i.e. code-hinting working perfectly). The web project DOES NOT have the service reference in the solution explorer.
When I added the reference, the sections were added to the app.config file (see below) and I copied them to the web.config in the web project.
How do I 'recreate' the web.config settings in the appsettings and Startup?
When attempting to process a test payment, this line of code throws an exception:
TransactionProcessorClient proc = new TransactionProcessorClient("ITransactionProcessor");
I have also tried defining the endpoint manually just prior but the same error results:
System.ServiceModel.EndpointAddress theendpoint = new System.ServiceModel.EndpointAddress("https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor");
TransactionProcessorClient proc = new TransactionProcessorClient("ITransactionProcessor", theendpoint);
This is the error:
An Exception occurred while trying to process your payment. Please try again. Could not find endpoint element with name 'ITransactionProcessor' and contract 'CybersourceTrxnProcessor.ITransactionProcessor' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.
Here is what config file looks like, generated when I added the service reference to the project in Visual Studio (and also matches what's in an older MVC project):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ITransactionProcessor">
<security mode="TransportWithMessageCredential" />
</binding>
<binding name="ITransactionProcessor1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor" binding="basicHttpBinding" bindingConfiguration="ITransactionProcessor"
contract="CybersourceTrxnProcessor.ITransactionProcessor" name="portXML" />
</client>
</system.serviceModel>
This is the appsettings.json:
"ITransactionProcessor": {
"security": { "mode": "TransportWithMessageCredential" },
"client": {
"endpoint": {
"address": "https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor",
"binding": "basicHttpBinding",
"bindingConfiguration": "ITransactionProcessor",
"contract": "CybersourceTrxnProcessor.ITransactionProcessor",
"name": "portXML"
}
}
}
This is what I have in Startup.cs (also need to set the Security mode to TransportWithMessageCredential as prescribed by Cybersource docs):
services.AddScoped<ITransactionProcessor>(provider => {
var client = new TransactionProcessorClient();
client.Endpoint.Address = new EndpointAddress(Configuration["ITransactionProcessor:client:endpoint:address"]);
client.Endpoint.Contract = new System.ServiceModel.Description.ContractDescription(Configuration["ITransactionProcessor:client:endpoint:contract"]);
client.Endpoint.Binding = new System.ServiceModel.BasicHttpBinding();
client.Endpoint.Name = "portXML";
return client;
});
Just FYI, I finally figured this out. Everything I had was correct except ONE tiny thing (doesn't it almost always come down to something simple). The error actually told me exactly what it needed. I simply needed to change my appsettings.json like so:
"name": "portXML"
to
"name": "ITransactionProcessor"

How to check wcf service is connected with client

I have created a WCF service in c#. Iam able to access it form a client app with url http://localhost:8080/classname/function
Is there any way to detect whether the client is connected from WCF service in which the client has the url http://localhost:8080 alone without class name and function name where the WCF service is listening to 8080 port at localhost?
Based upon your request to explain how to send an empty string message to a WCF service I have developed a VS2017 solution and uploaded onto the GITHUB for you.
The solution contain 3 projects, 2 on the WCF Service side (Class library and Console Application) and 1 for client.
WCF Service Side
First we define a ServiceContract that will have single OperationalContract to receive the string message and returns a boolean:
[ServiceContract]
public interface IClientConnectionService
{
[OperationContract]
bool Connect(string message);
}
Next, we have a class that implements this ServiceContract
public class ClientConnectionService : IClientConnectionService
{
public bool Connect(string message)
{
/*
* As per your comment on http://stackoverflow.com/questions/43366101/how-to-check-wcf-service-is-connected-with-client?noredirect=1#comment74005120_43366101
* the message should be empty, however you can pass string.
* Once you are done with processing you can return true or false depending upon how you want to carry out
* this operation.
*/
return true;
}
}
Next, we have the WCF service host manager (a console based application just to host this WCF service)
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ClientConnectionService)))
{
host.Open();
Console.WriteLine($"{host.Description.Name} is up and listening on the URI given below. Press <enter> to exit.");
PrintServiceInfo(host.Description);
Console.ReadLine();
}
}
private static void PrintServiceInfo(ServiceDescription desc)
{
foreach (ServiceEndpoint nextEndpoint in desc.Endpoints)
{
Console.WriteLine(nextEndpoint.Address);
}
}
}
Its responsibility is to just keep the WCF service listening for incomming requests on a net.tcp port defined in the config file:
<system.serviceModel>
<services>
<service name="StackOverflow.Wcf.Services.ClientConnectionService">
<endpoint
address="net.tcp://localhost:9988/ClientConnectionService/"
binding="netTcpBinding"
contract="StackOverflow.Wcf.Services.Contracts.IClientConnectionService"
></endpoint>
</service>
</services>
</system.serviceModel>
Once this is completed we have a running WCF service. Now lets turn our attention to the client that will consume this service.
WCF Client Side
This is just a console application that has a reference of WCF Service and it creates the proxy class to call the method on the service.
public class ClientConnectionServiceProxy : ClientBase<IClientConnectionService>
{
public bool Connect(string message)
{
return base.Channel.Connect(message);
}
}
Notice that we have used IClientConnectionService interface / contract from the service side. ClientBase<T> is a WCF Framework class.
Here is the program class that calls the WCF servie using above defined proxy class.
class Program
{
static void Main(string[] args)
{
using (ClientConnectionServiceProxy proxy = new ClientConnectionServiceProxy())
{
bool isCallSuccessful = proxy.Connect(string.Empty);
}
}
}
and here is the client configuration:
<system.serviceModel>
<client>
<endpoint
address="net.tcp://localhost:9988/ClientConnectionService/"
binding="netTcpBinding"
contract="StackOverflow.Wcf.Services.Contracts.IClientConnectionService"
></endpoint>
</client>
</system.serviceModel>
How to run:
Once you have downloaded the source code from GITHUB, open the StackOverflow.Wcf.sln file in VS2017 (thats what I have used to develop this - not sure if you can open it in the VS2015) and hit F5. You can put break points to step through the code and edit it as you wish.
Hope this makes it clear - leave any questions in the comments below.

Relationship between SVC files and WCF projects?

When creating a WCF project, the default member files are just ordinary csharp class files, rather than svc files. Are svc files required with a WCF project? When should they be used?
.svc files are used when you host your WCF service in IIS.
See Microsoft's doc here and here.
There's a module within IIS that handles the .svc file. Actually, it is the ASPNET ISAPI Module, which hands off the request for the .svc file to one of the handler factory types that has been configured for ASPNET, in this case
System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
If you are hosting your WCF service in something other than IIS, then you don't need the .svc file.
If you are using .net 4.0 or later, you can now "simulate" the .svc via config with the following:
<system.serviceModel>
<!-- bindings, endpoints, behaviors -->
<serviceHostingEnvironment >
<serviceActivations>
<add relativeAddress="MyService.svc" service="MyAssembly.MyService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
Then you don't need a physical .svc file nor a global.asax
It is possible to create a WCF project and host it in IIS without using a .svc file.
Instead of implementing your DataContract in your svc code-behind, you implement it in a normal .cs file (i.e. no code behind.)
So, you would have a MyService.cs like this:
public class MyService: IMyService //IMyService defines the contract
{
[WebGet(UriTemplate = "resource/{externalResourceId}")]
public Resource GetResource(string externalResourceId)
{
int resourceId = 0;
if (!Int32.TryParse(externalResourceId, out resourceId) || externalResourceId == 0) // No ID or 0 provided
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
return null;
}
var resource = GetResource(resourceId);
return resource;
}
}
Then comes the thing making this possible. Now you need to create a Global.asax with code-behind where you add an Application_Start event hook:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
// Edit the base address of MyService by replacing the "MyService" string below
RouteTable.Routes.Add(new ServiceRoute("MyService", new WebServiceHostFactory(), typeof(MyService)));
}
}
One nice thing about this is that you don't have to handle the .svc in your resource URLs. One not so nice thing is that you now have a Global.asax file.

Categories