Self hosting Web API - c#

My program is for Self hosting a Web API in a Windows Application.
As I dont have much experience in web servie, I request someone's help to fix the problem.
I could make a console application successfully.
But when I change it in to Windows application, my IDE goes stuck with Error "The application is in Break Mode".
Console Program;
using System.Web.Http;
using System.Web.Http.SelfHost;
Main Function:
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
using (HttpSelfHostServer server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit."); // Removed in Win Form
Console.ReadLine(); // Removed in Win Form
}
API Controller Class; I need to receive data here from "FromBody" attribute.
public class FieldsController : ApiController
{
[HttpPost]
public void PostAction([FromBody] Field model)
{
int patientID;
string name;
string age;
patientID = model.patientID;
name = model.patientName;
age = model.patientAge;
}
}

Can you put your error in more detail ?
I have also done in similar way and my one is working.
In the solution of the project I added a new class libraray type project for the webapi.
From the main windowsForm application I am intatiating the webapi.
The webapi project is as below:
using System;
using System.ServiceModel;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace MYWebAPI
{
public class WebApiProgram : IDisposable
{
private string URL = "http://localhost:1234/";
private HttpSelfHostServer server;
public WebApiProgram()
{
}
public WebApiProgram(string url)
{
this.URL = url;
}
public void Dispose()
{
server.CloseAsync().Wait();
}
public void Start()
{
var config = new HttpSelfHostConfiguration(URL);
config.TransferMode = TransferMode.Streamed;
AddAPIRoute(config);
server = new HttpSelfHostServer(config);
var task = server.OpenAsync();
task.Wait();
//Console.WriteLine("Web API Server has started at:" + URL);
//Console.ReadLine();
}
private void AddAPIRoute(HttpSelfHostConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
From the main() [program.cs file] winform applition I call the webapi start function.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main(string[] args)
{
//Start the webAPI
WebApiProgram.StartMain();
Thread.CurrentThread.CurrentCulture
= Thread.CurrentThread.CurrentUICulture
= CultureInfo.InvariantCulture;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = new MainForm();
Application.Run(mainForm);
}
}

Related

How do I make System.Net.HttpListener long running, or is there a better way?

Application
I’m working on an IIoT project. I need to be able to receive simple messages (json payload) on a piece of equipment whose HMI runs on Win7. We use 3rd party MQTT for this in some cases but for this situation I’m looking at HTTP, specifically a tiny http server in C# (.NET Framework 4.0). There doesn’t need to be a front end, just an api with a single endpoint.
Approach
I’ve found System.Net.HttpListener and got it doing what I want in a console app for initial testing. Problem is I’m using the GetContext method which blocks the main thread. Intuitively, this is not what I want, but I don’t know a more reasonable approach. I see there is a built in async method, HttpListener.BeginGetContext, but is that enough to do what I want? Ultimately looking to wrap this up into a service using the TopShelf NuGet package.
Not looking for a code-complete solution, but the proper conceptual approach.
you may use the Microsoft.Owin package to host http controllers in the console application
The idea is that you do not need the IIS to be installed in such case.
And you may host the application as windows service (using the TopShelf) if required.
However you are getting the ASP controllers working and should not bother with HttpListeners any more.
There are a lot of how-to guides available, like here
In few words you have to
add Microsoft.AspNet.WebApi.OwinSelfHost package
Configure the Owin in the code like
public class SelfHostStart
{
private readonly WebSettings config;
public SelfHostStart(WebSettings config)
{
this.config = config;
}
void Start<T>(string hostAddress, string message)
{
if (string.IsNullOrEmpty(hostAddress))
throw new ArgumentException("hostAddress", $"no value to start host {message}");
var oh = new OwinHost();
server = oh.Start<T>(hostAddress, message);
}
public void Start()
{
if (config?.Enabled == true)
{
Start<Startup>(config.HostAddress, "webServer");
}
}
}
public class WebSettings
{
public bool Enabled { get; set; }
public string HostAddress { get; set; }
}
then you may to create Startup.cs to configure http hosting like below:
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = GetHttpConfig(appBuilder);
ConfigureWebApi(appBuilder, config);
DEBUG
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
if
appBuilder.UseCors(CorsOptions.AllowAll);
appBuilder.UseWebApi(config);
}
public HttpConfiguration GetHttpConfig(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
// you may override some default behavior here
//config.Services.Replace(typeof(IHttpControllerActivator), new UnityControllerActivator());
//config.Services.Add(typeof(System.Web.Http.ExceptionHandling.IExceptionLogger), new ExceptionLog(appBuilder.CreateLogger<ExceptionLog>()));
//config.Services.Replace(typeof(System.Web.Http.ExceptionHandling.IExceptionHandler), new CustomExceptionHandler()); // to make ExceptionHandler works
//config.MessageHandlers.Add(new LoggingHandler(appBuilder.CreateLogger("rest")));
return config;
}
public void ConfigureWebApi(IAppBuilder app, HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.EnsureInitialized();
}
}
and controller (class with methods that are available for clients of a service):
[AllowAnonymous]
public class StatusController : ApiController
{
public StatusController()
{
}
[Route("status")]
public StatusDto Get()
{
var res = new StatusDto
{
DbVersion = "123",
LocalCurrency = "USD",
SwiftCode = "12345", // just sample
};
return res;
}
}
public class StatusDto
{
public string DbVersion { get; set; }
public string LocalCurrency { get; set; }
public string SwiftCode { get; set; }
}
and start the http server in your console
var config = new WebSettings() { HostAddress = "http://localhost:8080" };
var start = new SelfHostStart(config);
start.Start();
You should provide permissions for your console to listen a http url using the netsh http add urlacl command like
netsh http add urlacl url=http://+:8080/ user=everyone
Then you can browse the http://localhost:8080/status and should get report from the http console server.
You may extend you service by creating new controllers with methods

Cannot see my controller in self hosted WebApi project

I created a self hosted web api app, to run as a Windows Service, using TopShelf, and Autofac for dependency injection.
Here is my StartUp logic:
public class ApiShell : IApiShell
{
public void Start()
{
using (WebApp.Start<Startup>("http://localhost:9090"))
{
Console.WriteLine($"Web server running at 'http://localhost:9090'");
}
}
internal class Startup
{
//Configure Web API for Self-Host
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
GlobalConfiguration.Configuration
.EnableSwagger(c => c.SingleApiVersion("v1", "Swagger UI"))
.EnableSwaggerUi();
//default route
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
app.UseWebApi(config);
}
}
}
And I start the WebApp as follow:
public class HostService
{
//when windows service statrts
public void Start()
{
IoC.Container.Resolve<IApiShell>().Start(); //start web app
IoC.Container.Resolve<IActorSystemShell>().Start();
}
//when windows service stops
public void Stop()
{
IoC.Container.Resolve<IActorSystemShell>().Stop();
}
}
TopShelf configuration:
HostFactory.Run(x =>
{
x.Service<HostService>(s =>
{
s.ConstructUsing(name => new HostService());
s.WhenStarted(sn => sn.Start());
s.WhenStopped(sn => sn.Stop());
});
x.RunAsLocalSystem();
x.SetDescription("Sample Service");
x.SetDisplayName("Sample Service");
x.SetServiceName("Sample Service");
});
My controller:
public class PingController : ApiController
{
private IActorSystemShell _actorSystem;
public PingController(IActorSystemShell actorSystem)
{
_actorSystem = actorSystem;
}
[HttpGet]
public async Task<string> Ping()
{
var response = await _actorSystem.PingActor.Ask<PingMessages.Pong>(PingMessages.Ping.Instance(),
TimeSpan.FromSeconds(10));
return response.PongMessage;
}
}
I installed Swagger as well, but I can't reach my controller, using either of the following attempts:
http://localhost:9090/api/Ping
http://localhost:9090/swagger
What am I missing?
You can't just do this:
using (WebApp.Start<Startup>("http://localhost:9090"))
{
Console.WriteLine($"Web server running at 'http://localhost:9090'");
}
After the write line, there's no more statements left in the using, so the using will close, thus stopping the web app. This is one of those cases where even though the result of WebApp.Start is an IDisposable, you shouldn't use a using statement. Instead, do this:
public class ApiShell : IApiShell
{
_IDisposable _webApp;
public void Start()
{
_webApp = WebApp.Start<Startup>("http://localhost:9090");
Console.WriteLine($"Web server running at 'http://localhost:9090'");
}
public void Stop()
{
_webApp.Dispose();
}
}
public class HostService
{
public void Start()
{
IoC.Container.Resolve<IApiShell>().Start(); //start web app
}
public void Stop()
{
IoC.Container.Resolve<IApiShell>().Stop(); //stop web app
}
}
You haven't shown your dependency registration, but make sure that IApiShell is registered as a singleton so you're starting/stopping the same instance.
Note, if this were a traditional console app instead of a Windows service, you could do this:
using (WebApp.Start<Startup>("http://localhost:9090"))
{
Console.WriteLine($"Web server running at 'http://localhost:9090'");
Console.WriteLine("Press any key to exit.");
Console.ReadKey(true);
}
The ReadKey method would keep the using statement active and thus keep the web app from disposing.

Host C# rest service - Error on return type

I'm trying to host a Rest service i created in c#, deploying a .dll file and importing it in a new project with main method.
To create an host in the main I created an interface for my Rest service, but after the declaration of this interface i keep getting the error
"The name Content does not exist in the current context"
. Content is the return of my method, that should return only void or Task or Task T> because is async method.
How should I resolve this error?
This is part of the controller(Rest Service):
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.ServiceModel;
using Couchbase;
using Couchbase.Core;
using Couchbase.IO;
using JWT;
[ServiceContract]
public interface ApiController
{
Task<IHttpActionResult> SignUp(LoginModel model);
}
namespace Nerder_Backend.Controllers
{
[RoutePrefix("api/user")]
public class UserController : ApiController
{
private readonly IBucket _bucket = ClusterHelper.GetBucket(ConfigurationManager.AppSettings.Get("CouchbaseUserBucket"));
private readonly string _secretKey = ConfigurationManager.AppSettings["JWTTokenSecret"];
[Route("signup")]
[HttpPost]
public async Task<IHttpActionResult> SignUp(LoginModel model)
{
if (model == null || !model.IsValid())
{
return Content(HttpStatusCode.BadRequest, new Error("Invalid email and/or password"));
}
var userKey = CreateUserKey(model.Email);
if (await _bucket.ExistsAsync(userKey))
{
return Content(HttpStatusCode.Conflict, new Error($"email '{model.Email}' already exists"));
}
var userDoc = new Document<User>
{
Id = userKey,
Content = new User
{
Email = model.Email,
Password = CalcuateMd5Hash(model.Password)
},
Expiry = model.Expiry
};
var result = await _bucket.InsertAsync(userDoc);
if (!result.Success)
{
return Content(HttpStatusCode.InternalServerError, new Error(result.Message));
}
var data = new
{
token = BuildToken(model.Email)
};
var context = $"Created user with ID '{userKey}' in bucket '{_bucket.Name}' that expires in {userDoc.Expiry}ms";
return Content(HttpStatusCode.Accepted, new Result(data, context));
}
This is the main method :
namespace MockServer
{
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/signup");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(UserController), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
}
Update main to host the web api properly.
include the following using statements
using System.Web.Http;
using System.Web.Http.SelfHost; //install the nuget package Microsoft.AspNet.WebApi.SelfHost
update Program class to Host the Web API
class Program {
static void Main(string[] args) {
var baseAddress = "http://localhost:8080";
var config = new HttpSelfHostConfiguration(baseAddress);
config.MapHttpAttributeRoutes();//map attribute routes
// Create the server
using (var server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
}
}
}
Now make sure the api controller it correctly defined.
[RoutePrefix("api/user")]
public class UserController : ApiController {
private readonly IBucket _bucket = ClusterHelper.GetBucket(ConfigurationManager.AppSettings.Get("CouchbaseUserBucket"));
private readonly string _secretKey = ConfigurationManager.AppSettings["JWTTokenSecret"];
[HttpPost]
[Route("signup")] //Matches POST api/user/signup
public async Task<IHttpActionResult> SignUp(LoginModel model) {
//...code removed for brevity
}
}
Given the program set and controller attribute routes, the action would be found at
POST http://localhost:8080/api/user/signup

OWIN Self-Host + Web Api connection cant make requests through Postman

I am trying to self-host Web API. It works fine when I call requests through my program, where is API controller. But i can't make request through Postman Client. What could be the problem?
Api Controller
public class MyApiController : ApiController
{
public string Get()
{
return "Get";
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:44300/";
using (WebApp.Start<Startup>(url))
{
var client = new HttpClient();
var response = client.GetAsync(url + "api/myapi").Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
Console.ReadLine();
}
}
It looks like your issues are in your main method. In C#, the using statement (link) creates a resource, executes the code in the block, and then disposes of the resource.
In your posted example, your WebApp is disposed right after it prints the response to the console (and before you're able to make requests with your browser).
These edits should allow you to keep the WebApp in-scope in order to play around with the framework.
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:44300/";
using (WebApp.Start<Startup>(url))
{
var client = new HttpClient();
var response = client.GetAsync(url + "api/myapi").Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine("WebApp Ready");
Console.ReadLine();
}
Console.WriteLine("WebApp disposed.");
Console.ReadLine();
}
}

What is the equivalent of HttpServiceHost in ASP.NET WebAPI?

I wanted to try out this example of a self-hosted webservice (originally written in WCF WebApi), but using the new ASP.NET WebAPI (which is the descendant of WCF WebApi).
using System;
using System.Net.Http;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Microsoft.ApplicationServer.Http;
namespace SampleApi {
class Program {
static void Main(string[] args) {
var host = new HttpServiceHost(typeof (ApiService), "http://localhost:9000");
host.Open();
Console.WriteLine("Browse to http://localhost:9000");
Console.Read();
}
}
[ServiceContract]
public class ApiService {
[WebGet(UriTemplate = "")]
public HttpResponseMessage GetHome() {
return new HttpResponseMessage() {
Content = new StringContent("Welcome Home", Encoding.UTF8, "text/plain")
};
}
}
}
However, either I haven't NuGotten the right package, or HttpServiceHost is AWOL. (I chose the 'self hosting' variant).
What am I missing?
Please refer to this article for self-hosting:
Self-Host a Web API (C#)
The complete rewritten code for your example would be as follows:
class Program {
static void Main(string[] args) {
var config = new HttpSelfHostConfiguration("http://localhost:9000");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
using (HttpSelfHostServer server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
Console.WriteLine("Browse to http://localhost:9000/api/service");
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
public class ServiceController : ApiController {
public HttpResponseMessage GetHome() {
return new HttpResponseMessage() {
Content = new StringContent("Welcome Home", Encoding.UTF8, "text/plain")
};
}
}
Hope this helps.

Categories

Resources