Problems with deploying Service Fabric project with Owin to Azure Service Fabric Cluster - azure-service-fabric

I am working on a Service Fabric project with Owin, and I'm having troubles getting it deployed into the cloud. I have searched for others with the same problem, but I only found an answer telling that the error in the cluster tells where in the code it goes wrong. I have followed Microsofts Owin tutorial on how to write the method that fails, but with no luck.
I can run the project on Localhost direcly from Visual Studio, but the problem starts when I deploy it to a Service Fabric cluster in Azure. I have a 5 node cluster running, and when I deploy to it, it starts giving warnings after 2 minutes, and errors after 5 minutes. the status of the application is "inbuild".
Image of warning and Image of error.
I have two services, and the error from my cluster gives the error in these two methods(the same method in each service(OpenAsync)):
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
var serviceEndpoint =
_parameters
.CodePackageActivationContext
.GetEndpoint("ServiceEndpoint");
var port = serviceEndpoint.Port;
var root =
String.IsNullOrWhiteSpace(_appRoot)
? String.Empty
: _appRoot.TrimEnd('/') + '/';
_listeningAddress = String.Format(
CultureInfo.InvariantCulture,
"http://+:{0}/{1}",
port,
root
);
_serverHandle = WebApp.Start(
_listeningAddress,
appBuilder => _startup.Configuration(appBuilder)
);
var publishAddress = _listeningAddress.Replace(
"+",
FabricRuntime.GetNodeContext().IPAddressOrFQDN
);
ServiceEventSource.Current.Message("Listening on {0}", publishAddress);
return Task.FromResult(publishAddress);
}
the error from the cluster tells the error is in this section:
_serverHandle = WebApp.Start(
_listeningAddress,
appBuilder => _startup.Configuration(appBuilder)
);
the other method(from the other service):
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
var serviceEndpoint =
_parameters
.CodePackageActivationContext
.GetEndpoint("ServiceEndpoint");
var port = serviceEndpoint.Port;
var root =
String.IsNullOrWhiteSpace(_appRoot)
? String.Empty
: _appRoot.TrimEnd('/') + '/';
_listeningAddress = String.Format(
CultureInfo.InvariantCulture,
"http://+:{0}/{1}",
port,
root
);
try
{
_serverHandle = WebApp.Start(
_listeningAddress,
appBuilder => _startup.Configuration(appBuilder)
);
}
catch (Exception e)
{
Console.WriteLine(e);
throw e;
}
var publishAddress = _listeningAddress.Replace(
"+",
FabricRuntime.GetNodeContext().IPAddressOrFQDN
);
ServiceEventSource.Current.Message("Listening on {0}", publishAddress);
return Task.FromResult(publishAddress);
}
the error from the cluster tells the error is in this section:
try
{
_serverHandle = WebApp.Start(
_listeningAddress,
appBuilder => _startup.Configuration(appBuilder)
);
}
catch (Exception e)
{
Console.WriteLine(e);
throw e;
}
My StartUp Classes:
public void Configuration(IAppBuilder appBuilder)
{
var corsAttr = new EnableCorsAttribute(origins: "*", headers: "*", methods: "*");
var config = new HttpConfiguration();
config.WithWindsorSetup();
config.WithJsonSetup();
config.MapHttpAttributeRoutes(); //Enable Attribute-routing
config.WithSwaggerSetup();
config.EnsureInitialized();
config.EnableCors(corsAttr);
appBuilder.UseWebApi(config);
}
and where I create a new OwenCommunicationListener:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] {
new ServiceInstanceListener(initParams => new OwinCommunicationListener("", new Startup.Startup(), initParams))
};
}
I would very much like to be able to deploy it to Azure Service Fabric Cluster without any errors. Have a nice day, and thanks for helping.

you need to write your own custom class that configure the routing and http configuration for Owin listener.
Here is the class which I am using to configure the routing, try with it:
/// <summary>
/// This is the startup class that configure the routing and http configuration for Owin listener.
/// </summary>
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)
{
appBuilder.UseCors(CorsOptions.AllowAll);
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
config.Formatters.Remove(config.Formatters.XmlFormatter);
appBuilder.UseWebApi(config);
}
}
pass this class as an action to instance where you are creating instance of OwinCommunication Listener. Here is my code
endpoints.Select(endpoint => new ServiceInstanceListener(
serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext,
null, endpoint), endpoint));
This approach is working for me. Try with it hopefully it will work for you too

problem is solved. I edited this code:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return Context.CodePackageActivationContext.GetEndpoints()
.Where(endpoint => endpoint.Protocol.Equals(EndpointProtocol.Http) || endpoint.Protocol.Equals(EndpointProtocol.Https))
.Select(endpoint => new ServiceInstanceListener(serviceContext => new OwinCommunicationListener("", new Startup.Startup(), serviceContext)));
//return new[] {
// new ServiceInstanceListener(initParams => new OwinCommunicationListener("", new Startup.Startup(), initParams))
//};
}

Related

I ran my program as BackgroundService but the service does not run the code

I've created program in C# Worker Service .NET 7.0. This program listening all incoming GET request and save this requests as URI and URL into Log.txt file. I used Titanium Web Proxy to listen all incoming requests from server. Program is working correctly but problem apear when im trying to run it as windows service. To do this im publishing my program into folder, next using command prompt Im creating service choosing .exe file from published folder and starting process with "sc start ServiceName" command. Service is running but its not working at all.
Here is my Program.cs code:
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Extensions.Hosting;
using Serilog.Extensions.Logging;
namespace WorkerService5
{
public class Program
{
static async Task Main(string[] args)
{
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.UseWindowsService()
.UseSerilog()
.Build();
await host.RunAsync();
}
}
}
Here is my Worker.cs code:
using System.Net;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
using Serilog;
using Microsoft.Extensions.Hosting;
namespace WorkerService5
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public Worker(ILogger<Worker> logger, IHostApplicationLifetime hostApplicationLifetime)
{
_logger = logger;
_hostApplicationLifetime = hostApplicationLifetime;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () =>
{
try
{
var proxyServer = new ProxyServer(userTrustRootCertificate: false);
proxyServer.BeforeRequest += OnRequest;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true);
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
foreach (var endPoint in proxyServer.ProxyEndPoints)
_logger.LogInformation("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
// Console.ReadLine();
stoppingToken.Register(() =>
{
proxyServer.BeforeRequest -= OnRequest;
proxyServer.Stop();
});
await Task.Delay(Timeout.Infinite, stoppingToken);
}
catch (Exception)
{
throw;
}
});
private async Task OnRequest(object sender, SessionEventArgs e)
{
var filePath = #"E:\LogiusService\Logs\log.txt";
Log.Logger = new LoggerConfiguration()
.WriteTo.File(filePath, rollingInterval: RollingInterval.Day, shared: true)
.CreateLogger();
string requestedUrl = e.HttpClient.Request.Host;
Console.WriteLine("Requested URL: " + requestedUrl);
Log.Information($"Requested URL: " + requestedUrl);
string requestedUri = e.HttpClient.Request.RequestUri.AbsoluteUri;
Console.WriteLine("Requested URI: " + requestedUri);
Log.Information($"Requested URI: " + requestedUri);
}
}
}
Any ideas how can i run it as Windows Background Service?
Cheers!

Port sharing using Http.sys in Azure service fabric

Want to host 2 asp .net core 2 websites in Azure service fabric which use port 80. In this link they mentions port sharing but not sure how to configure this? is there a way to mention host name?
Use this class:
public class HttpSysInstanceListener
{
public static ServiceInstanceListener[] CreateListener(Type startupType, string endpointName, string rootPath, int port)
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new HttpSysCommunicationListener(serviceContext, $"{endpointName}", (url, listener) =>
{
return new WebHostBuilder()
.UseHttpSys(options =>
{
options.UrlPrefixes.Add($"http://+:{port}/{rootPath}/");
options.Authentication.Schemes = Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
})
.ConfigureServices(services => services
.AddSingleton<StatelessServiceContext>(serviceContext)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup(startupType)
.UseUrls(url)
.UseApplicationInsights()
.Build();
}))
};
}
}
If this is a .NET CORE stateless WEB Service API then use HttpSys. However, HttpsSys can be only used with IIS and not KESTREL.
To use HttpSys in a stateless service, override the CreateServiceInstanceListeners method and return a HttpSysCommunicationListener instance:
C#
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
new WebHostBuilder()
.UseHttpSys()
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseStartup<Startup>()
.UseUrls(url)
.Build()))
};
}
also change url to something different than the default:
.UseUrls(url+="WebService")
so for a localhost on port 80 the URL would be:
http(s)//localhost.com/WebService/api/foo
Microsoft Document

How to host the Windows Workflow as a Web service(.svc)?

I am trying to host the windows workflow as a web service, below is the sample workflow that I built and would like to host as a web service(.svc), can you please suggest the required steps?
using System;
using System.ServiceModel.Activities;
using System.Activities;
using System.ServiceModel;
using System.Activities.Statements;
namespace DemoWF
{
public class _25_LeaveRequest
{
public WorkflowService GetInstance()
{
WorkflowService service;
Variable<int> empID = new Variable<int> { Name = "empID" };
Variable<int> requestID = new Variable<int> { Name = "requestID" };
Receive receiveLeaveRequest = new Receive
{
ServiceContractName = "ILeaveRequestService",
OperationName = "ApplyLeave",
CanCreateInstance = true,
Content = new ReceiveParametersContent
{
Parameters ={
{"empID",new OutArgument<int>(empID)}
}
}
};
SendReply replyLeaveRequestID = new SendReply
{
Request = receiveLeaveRequest,
Content = new SendParametersContent
{
Parameters ={
{"requestID",new InArgument<int>(requestID)},
},
},
};
Sequence workflow = new Sequence()
{
Variables = { empID, requestID },
Activities = {
new WriteLine{Text="WF service is starting..."},
receiveLeaveRequest,
new WriteLine{
Text=new InArgument<string>(aec=> "Emp ID="+empID.Get(aec).ToString())
},
new Assign<int>{
Value=new InArgument<int>(5),
To=new OutArgument<int>(requestID)
},
new WriteLine{
Text=new InArgument<string>(aec=> "Request ID="+requestID.Get(aec).ToString())
},
replyLeaveRequestID
},
};
service = new WorkflowService
{
Name = "AddService",
Body = workflow
};
return service;
}
}
Right now, it is self hosted as highlighted below
namespace DemoWF
{
class Program
{
static void Main(string[] args)
{
LeaveRequest();
}
private static void LeaveRequest()
{
_25_LeaveRequest receiveAndReplyWorkflow = new _25_LeaveRequest();
WorkflowService wfService = receiveAndReplyWorkflow.GetInstance();
Uri address = new Uri("http://localhost:8000/WFServices");
WorkflowServiceHost host = new WorkflowServiceHost(wfService, address);
try
{
Console.WriteLine("Opening Service...");
host.Open();
Console.WriteLine("WF service is listening on " + address.ToString() + ", press any key to close");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine("some thing bad happened" + e.StackTrace);
}
finally
{
host.Close();
}
}
}
}
The quickest way would be to create a WCF Workflow Service Application.
You'll get a workflow designer where you can drag and drop the activities you need:
And if you run the project in Visual Studio, you'll get an auto-generated WSDL with your service operation(s):
And also it will bring up Visual Studio's WCF Test Client tool:
You can create a workflow-based service that handles multiple methods by using the Pick Branch activity. Each branch would then have a Receive and Send Reply activity, with the receive activity moved to the trigger section and the send reply activity in the action part.
Each trigger would be for a specific operation on the service. In the following example, I define two operations: MyFirstOperation and MySecondOperation.
Below is what the WCF test client tool will show with multiple operations exposed from the workflow:
Hopefully that gets you started. The topic of standing up workflow-based WCF services can get quite involved. :)

Setup a Azure Service Fabric Listener for Service Bus Relay Binding

I want to setup a Azure Service Bus Relay between my Service Fabric cluster hosted in Azure and my Private Network.
I can't get the configuration of the Service Bus Relay to work from a Service Fabric WCF service.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
// Standard WCF Listener
new ServiceInstanceListener(context =>
new WcfCommunicationListener<IFooService>(
wcfServiceObject: this,
serviceContext: context,
endpointResourceName: "WcfServiceEndpoint",
listenerBinding: WcfUtility.CreateTcpListenerBinding()
)
),
// Service Bus Relay Listener
new ServiceInstanceListener(context =>
{
var wcfRelay = new WcfCommunicationListener<IFooService>(
wcfServiceObject: this,
serviceContext: context);
wcfRelay.ServiceHost.AddServiceEndpoint(
typeof(IFooService),
new NetTcpRelayBinding(),
ServiceBusEnvironment.CreateServiceUri(
"sb",
"{mynamespace}.servicebus.windows.net",
"{myservce}"))
.Behaviors.Add(new TransportClientEndpointBehavior
{
TokenProvider =
TokenProvider.CreateSharedAccessSignatureTokenProvider(
"RootManageSharedAccessKey",
"{mykey}")
});
return wcfRelay;
})
};
}
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(context =>
{
var listener = new WcfCommunicationListener<IFooService>(
serviceContext: context,
wcfServiceObject: new FooService(),
listenerBinding: new WebHttpRelayBinding(EndToEndWebHttpSecurityMode.None,RelayClientAuthenticationType.None),
address: new EndpointAddress( ServiceBusEnvironment.CreateServiceUri("https","[ServiceBusNamespace]","[RelayEndPoint]"))
);
var ep = listener.ServiceHost.Description.Endpoints.Last();
ep.EndpointBehaviors.Add(
new TransportClientEndpointBehavior()
{
TokenProvider = TokenProvider.CreateSharedSecretTokenProvider("owner","[issuerSecret]")
});
ep.EndpointBehaviors.Add(
new ServiceRegistrySettings(discoveryType:DiscoveryType.Public));
ep.EndpointBehaviors.Add(new WebHttpBehavior());
return listener;
})
};
}

Web API HttpClient PutAsync returning Http 404

Im trying to send a PUT to my Web API and am struggling a bit as to how I should construct the actual Http request. Below is an integration test sample. It works fine using HttpMessageInvoker to call the Web API Put, but I want to use HttpClient in test also since that is what I'll be using in the business layer.
[TestMethod]
public void Verify_UpdateBudgetData_Http_PUT()
{
int budgetId = 1;
string appId = "DummyApp";
string userId = "Dummy";
string value = "400";
string filterJSON =
"{dimensionFilter:{\"Demo_Konto\":[\"3000\"],\"Demo_AO\":[\"200\"]},valueSpreadType:{\"Value1\":0}}";
HttpConfiguration config = new HttpConfiguration();
Konstrukt.SL.AggregationEngine.WebApiConfig.Register(config, new SL.AggregationEngine.AutofacStandardModule());
HttpServer server = new HttpServer(config);
/*this works*/
using (HttpMessageInvoker client = new HttpMessageInvoker(server))
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put,
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON)))
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Wrong http status returned");
}
};
/*this does not work*/
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri("http://localhost");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage =
client.PutAsync(
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON), new StringContent("")).Result;
Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode, "Wrong http status returned");
}
}
Here is my WebApiConfig-class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config, Autofac.Module moduleToAppend)
{
config.Routes.MapHttpRoute(
name: "UpdateBudgetData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{appId}/{userId}/{value}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetBudgetAndRefData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{userId}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.EnableCors();
config.EnableSystemDiagnosticsTracing();
// Autofac container
// if not configured here you'll not have dependencies provided to your WebApiControllers when called
var builder = new ContainerBuilder(); // yes, it is a different container here
builder.RegisterAssemblyTypes( // register Web API Controllers
Assembly.GetExecutingAssembly())
.Where(t =>
!t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
.InstancePerLifetimeScope();
// register your graph - shared
builder.RegisterModule(
new AutofacStandardModule()); // same as with ASP.NET MVC Controllers
if (moduleToAppend != null)
{
builder.RegisterModule(moduleToAppend);
}
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(
container);
}
public static void Register(HttpConfiguration config)
{
Register(config, null);
}
}
How can I fix the HttpClient call to PutAsync? Should I embed the FilterJSON parameter in the body? If so, how to do that? I've tried that but then the FromBody parametger was null...
I got it working by using the FromBody tag in the controller and then wrapping that parameter in the http request body. An important note to is to prefix the parameter with an "=" sign to make sure it was interpreted correctly by the controller. Also I removed the same parameter from the route config. Finally to make the client to server request work I had to replace HttpServer Class with httpselfhostserver