I'm not sure if this is a question that should be asked on SO, but I can't think of a better place to ask it. What I want to know is: Does anyone out there actually use the Web API 4.5.2 Template that ships with Visual Studio. I've been writing C# Web Applications for some time now, and I feel like these templates are bloated nightmares to work with. They also seem to push developers away from understanding how things are actually working under the hood. Do you agree or disagree, and should I be using these templates if I want to call myself a .Net Web Developer?
This is ofcourse highly opinionated, but yes I agree; the default (MVC related) templates are bloated with unnecessary features and there's nothing lean and mean about them. I've used a custom template for a long time just to get around this (frustrating) problem.
Note that Visual Studio now has the option to create an empty website and gives you the option to only include WebAPI, not forcing you to include ASP.NET MVC. This produces a nice tiny project.
If you do experiment alot with WebAPI and you don't want your harddisks to be a cluster bomb full of Visual Studio projects, I can highly recommend LinqPad. With a tiny bit of code you can create a self hosting process and use it as a template. For example:
#define NONEST
void Main()
{
string baseAddress = "http://localhost:9000/";
try
{
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
// Create HttpCient and make a request to api/values
HttpClient client = new HttpClient();
var response = client.GetAsync(baseAddress + "api/values").Result;
Console.WriteLine("response: " + response);
Console.WriteLine("result: " + response.Content.ReadAsStringAsync().Result);
}
}
finally
{
// LinqPad keeps the AppDomain running to reduce compile time.
// Force app domain unload (Displays "Query ended unexpectedly")
// You can also press shift-F5 to unload the AppDomain.
AppDomain.Unload(AppDomain.CurrentDomain);
}
}
// Define other methods and classes here
public class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Related
I am trying to refactor an old setup, the end goal will be that I will have a functional REST API. I am getting rid of an old NuGet service and I sort of have to rebuild everything now. I am trying to set it up using ASP.NET Web API. This will include 60+ routes (like "www.website.com/cars/{id}/engine" "www.website.com/inventory/{id}" etc..)
I have tried attribute routing and conventional routing, and nothing seems to work. I get 404's no matter what I seem to try. I am probably just not doing either of them correctly.
Here is how I am setting up configuration (I am using a self hosting package btw but this is the main configuration point):
public class SomeHostClass
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//If I am trying attribute based routing, uncomment this
//config.MapHttpAttributeRoutes();
Register(config.Routes);
// I think I need this at all times?
app.UseWebApi(config);
}
public void Register(HttpRouteCollection routes)
{
// One of many other routes.MapHttpRoute() calls
routes.MapHttpRoute(
name: "Cars",
routeTemplate: "cars/{id}/engine",
defaults: new
{
controller = "CarController",
action = "Get"
},
constraints: null);
}
}
Then here is my Car Controller:
public class CarController : ApiController
{
[HttpGet]
public object Get([FromUri] CarDTO carObject)
{
// Some code calling a private worker method
return WorkerMethod();
}
private object WorkerMethod()
{
// Worker method do stuff, return
}
}
This Get() method is never called and a 404 is returned.
Another note: I have tried using reflection to register all the routes, which slims down the code. I'll debug it and it looks like all the routes have been configured correctly within the HttpConfiguration.routes but I will get 404's still. Even without reflection doing it the way shown above- if I debug and look at the values they all seem correct, but don't work.
Another Note: I should also mention that this is the error I usually get along with the 404.
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:8000/cars/1/engine'.","MessageDetail":"No type was found that matches the controller named 'CarController'."}
How do I get these routes recognized and stop returning 404's and start returning my data?
If this is a bad way of going about this- what's the best way?
Summary: I want to have an wehook url with a specific name for the webhook, rather than .../custom/ or .../genericjson/ if using those nuget packages.
i.e.
https://localhost:44300/api/webhooks/incoming/MyWebhook?code=1234567890
All the guides and the likes that I've found are either for existing Webhook nuget packages (Github, bitbucket, slack etc.) or the Generic and Custom one (that I don't really understand the difference of, besides the names, but thats another matter).
I got the genericjson one up and running and it's doing what it is supposed to, but I would much rather have a more correct url and/or name for the Webhook.
So how do I get (which I assume is what I need to get it working as I want)
config.InitializeReceiveGenericJsonWebHooks();
to instead work as if it were
config.InitializeReceiveMyWebhookWebHooks();
and thus my server listening for webhooks on the above URL (provided I add the key in appSettings and so on).
I tried looking at the definition of InitializeReceiveGenericJsonWebHooks() and InitializeReceiveGitHubWebHooks(), but there is barely any code there nor do I know how/what/where to create my own version of it.
I'm quite new to Asp.net (have previously mostly coded sites in html/css/php/javascript on Apache, rather than Asp.net/C# on IIS) so I recon there ought to be some easy way to solve it, but after an entire day searching and trying I'm at an end as to what to try next.
Edit:
Currently this is what my code (related to Webhook) looks like. Nothing special, just bare minimum to get the webhook working.
Web.config
<appSettings>
<add key="MS_WebHookReceiverSecret_GenericJson" value="1234567890123456789012345678901234567890"/>
</appSettings>
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//The revelant part to the question
config.InitializeReceiveGenericJsonWebHooks();
}
}
GenericJsonWebHookHandler.cs
namespace Project.API.Webhooks
{
public class GenericJsonWebHookHandler : WebHookHandler
{
public GenericJsonWebHookHandler()
{
this.Receiver = "genericjson";
}
public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
{
// Get JSON from WebHook
JObject data = context.GetDataOrDefault<JObject>();
//Do something with webhook
return Task.FromResult(true);
}
}
}
I finally managed to solve it.
By looking at the GitHub of AspNetWebHooks and further in to the GenericJSON WebHook that I'm using, I finally found out what was missing.
The only thing needed was create a file called HttpConfigurationExtensions.cs and copy+paste the code from the HttpConfigurationExtensions.cs source into the newely created file and change
public static void InitializeReceiveGenericJsonWebHooks(this HttpConfiguration config)
to what I wanted it to listen to
public static void InitializeReceiveMyWebhookWebHooks(this HttpConfiguration config)
like so.
Then I also needed to create a file called MyWebhookReceiver.cs and copy+paste the code from GenericJsonWebHookReceiver.cs and change the contents slightly
public class GenericJsonWebHookReceiver : WebHookReceiver
{
internal const string RecName = "genericjson";
into
public class MyWebhookWebHookReceiver : WebHookReceiver
{
internal const string RecName = "MyWebhook";
It still acts like GenericJSON webhook (which is what I wanted) but now I can use /api/incoming/MyWebhook/whateverIWant?code=123456790 as webhook URL instead of having it look like /genericjson/MyWebhook?code=1234567890.
You still need the handler file, but in the same way the two files were slightly changed here you can do the same with the handler. Create MyWebHookHandler.cs and just copy+paste the code, changing the GenericJson to MyWebHook and it should be good to go.
Nice work! If anyone else is following this, the only couple of thingf missing is the Web.config entry
<add key="MS_WebHookReceiverSecret_GenericJson" value="i=xxx,z=xxx" />
becomes:
<add key="MS_WebHookReceiverSecret_MyWebhook" value="i=xxx,z=xxx" />
As the last section of the key name needs to match the name of the new naming convention in the last section.
And the handler itself - on the constructor:
Should be:
public MyWebhookHandler()
{
this.Receiver = GenericJsonWebHookReceiver.ReceiverName;
}
not:
public MyWebhookHandler()
{
this.Receiver = "genericjson";
}
Also - there is no benefit doing the HttpConfigurationExtensions.cs step.
You do however need to to the GenericJsonWebHookReceiver.cs step for the process to work.
Assuming you forgot to tick the Web API checkbox (add it to the project) when making a new MVC (5) project, what do you need to do add Web API and get it working?
There are a bunch of migration questions, but none seemed to have the complete and up-to-date steps for adding Web API to an MVC 5 project and it seems to have changed from some of the old answers.
Add Web API to MVC 4
Adding GlobalConfiguration.Configure(WebApiConfig.Register) MVC 4
Update the MVC project
Use Nuget to get the newest Web API.
Project - Right click - Manage Nuget Packages - Search for Web API (Microsoft ASP.NET Web API ...) and install it to your MVC project.
Then you still need to get Web API routing to work.
From Microsoft's Configuring ASP.NET Web API 2
Add WebApiConfig.cs to the App_Start/ folder
using System.Web.Http;
namespace WebApplication1
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// TODO: Add any additional configuration code.
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
}
If you have an MVC Project it will have Global.asax.cs, add the new routes. Order of the Global.asax.cs routes is critical. Note there are outdated examples which use
WebApiConfig.Register
Add this line to Global.asax.cs:
GlobalConfiguration.Configure(WebApiConfig.Register);
protected void Application_Start()
{
// Default stuff
AreaRegistration.RegisterAllAreas();
// Manually installed WebAPI 2.2 after making an MVC project.
GlobalConfiguration.Configure(WebApiConfig.Register); // NEW way
//WebApiConfig.Register(GlobalConfiguration.Configuration); // DEPRECATED
// Default stuff
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
WebAPI Help
To get the (very) helpful WebAPI help pages, install WebAPI.HelpPage.
See http://channel9.msdn.com/Events/Build/2014/3-644 (~42 minutes in) for what it does. It looks very helpful!
Nuget Console: Install-Package Microsoft.AspNet.WebApi.HelpPage
To verify WebAPI is working:
To the controllers folder -> Add new item -> Web API Controller Class.
public class TestController : ApiController
{
//public TestController() { }
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
//...
}
Now you can test in IE/FF/Chrome as usual, or in the JavaScript consoles for non-get testing.
(With just the controller in the URL it will call the GET() action in the new Web API Controller, it's automatically mapped to methods/actions depending on the REST e.g. PUT/POST/GET/DELETE. You don't need to call them by action like in MVC)
The URL directly:
http://localhost:PORT/api/CONTROLLERNAME/
Alternatively use jQuery to query the controller.
Run the project, Open the console (F12 in IE) and try run an Ajax query. (Check your PORT & CONTROLLERNAME)
$.get( "http://localhost:PORT/api/CONTROLLERNAME/", function( data ) {
//$( ".result" ).html( data );
alert( "Get data received:" + data);
});
Side note: There are some pros/cons to consider when combining MVC and Web API in a project
WebAPI Help verification:
http://localhost:PORT/help
I have an existing .NET 4 console application which I want to start exposing a REST API. I'm using the Microsoft self host Web API library to have it expose that API, but I'm having trouble understanding how the routing path gets developed.
Here is an example of a controller, which should expose some database objects my console application already handles:
public class UserController : ApiController
{
UserInformation[] users;
public IEnumerable<UserInformation> GetAllUsers()
{
// snip.
}
public UserInformation GetUserById(int id)
{
// snip.
}
}
And I'm exposing my API in Program.cs like so:
var config = new HttpSelfHostConfiguration("http://localhost:8800");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config)) { // code }
Given the above code, I'd expect that I could get resources by making http requests like http://localhost:8800/api/users and http://localhost:8800/api/users/1, but those don't seem to work. How does the controller part of the GET request get created? It doesn't seem like users is the correct routing path for the API, but I'm not sure what goes there.
Thanks for any help
That's because your controller is called UserController and not UsersController. Either rename your controller to UsersController or modify your request to go to http://localhost:8800/api/user.
This should solve the problem.
I created one solution with two projects: one is a class library with a Self Host Web API (created with the help of http://www.asp.net/web-api/overview/hosting-aspnet-web-api/self-host-a-web-api), the second is a windows service created with TopShelf. The purpose of this solution is to have a status report on the service with the use of Web API.
Everything works fine, but when I recreate my solution within a target solution the whole application does not work properly. The Windows Service seems to be working, but when I type localhost:8080/Test which is suppose to view OK (and it does in the separate test solution mentioned at the beginning) it throws an error (viewed as an xml):
Message: No HTTP resource was found that matches the request URI 'http://localhost:8080/Test'.
MessageDetail: No type was found that matches the controller named 'Report'.
There is a ReportController (inheriting from ApiController) in the project that contains the SelfHost but somehow it's "visible". I took a guess (a stupid guess, I believe) and moved it to the windows service project but it's also not working.
Can someone tell me what is the problem I'm facing? Why does it not see the controller if it has in a simple solution?
EDIT:
My routing looks like this:
var config = new HttpSelfHostConfiguration(String.Format("http://localhost:{0}", port));
config.Routes.MapHttpRoute("API Default", "{action}", new { controller = defaultControllerName });
where
defaultControllerName = "Report";
I am ashamed to admit it, but the reason why it didn't work lay in the controller class not having an access modifier. Making it public fixed the bug.
the Class and method must be public
public class PrintController: ApiController
{
//[HttpGet, Route("api/Print/Getp")]
public string Get()
{
var ob = new List<string>();
foreach (var item in File.ReadLines(#"c:\PrintService\pr.txt"))
{
string i = item;
ob.Add(i);
}
var json1 = JsonConvert.SerializeObject(ob);
return "ok";
}
}
and my route config is this:
_config.Routes.MapHttpRoute("DefaultHttpRoute", "api/{controller}");