We have been developing a .NET MVC application and recently are looking to integrate a webhook for an email service. I'll admit, this is my first attempt at webhooks and Web API, but it looks fairly straight forward. I've followed several of the best practice and code examples from SendGrid and keep getting the "No type was found that matches the controller named 'xxxxxx'" message. I'm testing locally with Postman and can not get the controller(s) to be found. My initial goal is to test with the most basic configuration and just pass a POST to our web application, parse the data, and return 'ok'.
I've have enabled attribute routing in WebApiConfig.cs, have tested multiple different controller configurations, added "GlobalConfiguration.Configure(WebApiConfig.Register);" to my Global.asax.cs file, and made sure my classes are public.
Am I missing something? I've been troubleshooting this for several hours over multiple days and have not been able to figure it out.
In my postman request I am not sending any parameters, have the content type header set to jason, and am only including a sample SendGrid event in the body. I've verified the port number, and am not passing any authentication via http to our local application. The POST request is being sent to the following url: http://localhost:59998/api/sample
I've followed several stack overflow posts on similar issues and have made sure I'm not shooting myself in the foot (private classes, plural vs singular, api config settings).
My api config:
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 }
);
}
}
I've removed most of the action methods to simplify the code for the controller. All I'm looking to do right now is to accept a POST, debug locally, and return ok. I've tried with multiple different class types and have back-tracked to just the most simple options possible.
I have breakpoints set in my controller and I've been troubleshooting with multiple testing variables, which I've removed to clean up the code (example: int test = 0).
namespace StickerAppWeb.Controllers
{
public class SampleController : ApiController
{
[HttpPost]
public string Index()
{
return "API method";
}
public void Post()
{
//int test = 0; //breakpoint here
}
}
}
My Global config:
namespace StickerAppWeb
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Update1:
As a follow-up from yesterday, I also have this project published to Azure and get the same response from Postman when submitting a POST to the application in Azure. Is there any reason that the application is not finding that controller both locally, and in Azure?
Your code has no problem and should work. To send a post request to the api, you can follow the code below.
public string MYMethod()
{
string str= Task.Run(() => GetFromWeb).Result;
return str;
}
If you want to use the post method
private async Task<string> GetFromWeb()
{
HttpClient client = new HttpClient();
string Resturl = "domainname/api/sample/Index";
var response = await client.PostAsync(Resturl, null);
var result = await response.Content.ReadAsStringAsync();
return result;
}
If you want to use the get method
private async Task<string> GetFromWeb()
{
var client = new HttpClient();
var result = await client.GetAsync("domainname/api/sample/Index");
var response = await result.Content.ReadAsStringAsync();
return response;
}
Related
I need to add a very simple Web API to an existing library so that Python can communicate with the application. Simple Request/JSON response. This is more challenging than initially thought. I'm used to NodeJS where a library like Express can do this in a few lines of code.
Obviously the web server needs to be integrated in the library. I cannot be dependent on IIS or any web server.
These kinds of tutorials are all over the web:
https://github.com/jbogard/Docs/blob/master/aspnet/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api.md
Install: Microsoft.AspNet.WebApi.OwinSelfHost
Main
static void Main(string[] args)
{
string baseAddress = "http://localhost:9000/";
// 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);
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.ReadLine();
}
}
Startup
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);
}
}
Controller
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody] string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
It seems simple enough, however, it does not work in .NET 6. There seems to be compatibility issues.
I stumbled upon threads like the following ones:
Self Hosting OWIN in .NET Core
NullReferenceException experienced with Owin on Startup .Net Core 2.0 - Settings?
However I'm struggling to find a practical answer onhow to deploy a simple Web API in an existing .NET 6 library. The workaround suggested does not work for me.
Any advice will be appreciated ? Should I rather go for a different library? Is ASP.NET not the right tool to use ?
ASP.NET Core comes with build in and enabled by default web server - Kestrel so there is no need to set up OWIN. The simple setup can look this way (UseKestrel is called internally by WebApplication.CreateBuilder):
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
See also:
Host and deploy ASP.NET Core.
Servers
Use the ASP.NET Core shared framework
I am working on an api that allows managment of contacts.
I've generated my Entity Framework Context, Models, and Controllers.
My Api request are made in a Proxy class (called APIHelper) that contains methods to be used in my app.
My API has no problem returning results when calling the Get methods to fetch my list of Contacts, but when sending a PUT or POST request.
These Request actually work, the Instructions contained within each Method are executed correctly, PUT modifies my Contacts, POST creates a new Contacts, but reaching the end of either methods, right after it returns, the Program stops, or at least it looks like it.
There are no feedbacks from either the API which looks like it did its job, and the Proxy class which still seems to be awaiting the response from the API.
Here is the code doing the Request from the Proxy APIHandler Class :
public static async Task<bool> PostEdiContact(EdiContact ediContact)
{
string query = $"{ApiPred}/api/EdiContacts";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(query);
//HTTP POST
var result = await client.PostAsJsonAsync(query, ediContact);
return result.IsSuccessStatusCode;
}
}
Here is the code From the controller :
[ResponseType(typeof(EdiContact))]
public async Task<IHttpActionResult> PostEdiContact(EdiContact ediContact)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.EdiContacts.Add(ediContact);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException e)
{
if (EdiContactExists(ediContact.contactID))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = ediContact.contactID }, ediContact);
}
This last return is where the next execution just seems to go into the beyond as I have no clue about where it goes next. It works the fine for the Get Methods, but not these.
I have found no similar cases online and no one around me can help me with it.
As an additional ressources, here is the WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config
.Formatters
.JsonFormatter
.SerializerSettings
.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
// Removing Xml formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
}
Testing the request using swagger also returns an a response with code 201 so everything seems fine. but my Proxy Class doesn't get anything awaiting the response.
So I couldn't get it to work by using the PostAsJsonAsync() method from the HttpClient class.
What I did to work around this issue is using the basic SendAsync() method.
It is a bit more work since you need to declare and initialise an HttpRequestMessage, but here is what it looks like :
public static async Task<bool> PostEdiContact(EdiContact ediContact)
{
string json = JsonConvert.SerializeObject(ediContact);
using (HttpClient client = new HttpClient())
{
string url = $"{ApiPred}/api/EdiContact";
using(HttpRequestMessage request = new
HttpRequestMessage(HttpMethod.Post, url))
{
StringContent stringContent = new StringContent(
json,
Encoding.UTF8,
"application/json");
request.Content = stringContent;
HttpResponseMessage response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
throw new Exception("An error occured");
response.EnsureSuccessStatusCode();
return true;
}
}
}
Being a noob in MVC web api there is probably something very obvious I'm missing..
However, In my ProjectController I have the following method with attributes (I know this kind of method should be POST but just testing easily...):
[Route("api/projects/archive/{id:int}")]
[HttpGet]
public void Archive(int id)
{
_projectService.Archive(id);
}
However, when I open my URL such as:
http://localhost:49923/api/projects/archive/1
The page simply redirects to the current URL, and the archive method is not called. I also have a breakpoint at the method to verify it's not hit.
My best guess is I also have to update my web api route config which is the default, but I just assumed the route attribute was enough?
Here is my web api route config:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional});
config.Formatters.JsonFormatter.SupportedMediaTypes
.Add(new MediaTypeHeaderValue("text/html"));
}
What am I doing wrong here? :-)
EDITS:
Clearification 1 - my ProjectController:
public class ProjectsController : ApiController
{
private ProjectService _projectService;
public ProjectsController()
{
_projectService = new ProjectService();
}
[Route("api/projects/archive/{id:int}")]
[HttpGet]
public void Archive(int id)
{
_projectService.Archive(id);
}
}
Clearification 2 - redirect:
So lets say I stand on the homepage (/). I then go to the URL "http://localhost:49923/api/projects/archive/1", it will just reload page and leave my back at the home page.
The Web API config is configured correctly.
Ensure that the controller and the action are constructed properly
public class ProjectController : ApiController {
//...other code removed for brevity
[HttpGet]
[Route("api/projects/archive/{id:int}")]//Matches GET api/projects/archive/1
public IHttpActionResult Archive(int id) {
_projectService.Archive(id);
return Ok();
}
}
Its bit late to answer but hope you find it useful,
Many times the way how we write the code help us find the solution to the problem,
As Nkosi already answered, that the constructing controller and the action method properly will resolve the issue.
Its always helpful to check the method first rather then looking at the route.config because by default it will be the same unless you provide your custom attribute.
I am making a POST request, and getting a 404 - Not Found error back, to this controller and action:
[AllowAnonymous]
[System.Web.Mvc.RoutePrefix("api/Appt")]
public class AppointmentController : BaseController
{
[HttpPost]
[Route("")]
public AppointmentDto Post(AppointmentDto model)
{
Db.Appointments.Add(model);
Db.SaveChanges();
Logger.Info($"Appointment ID {model.Id} created.");
return model;
}
}
The request is made from a WPF client using HttpClient from the Microsoft.AspNet.WebApi.Client package. The client is configured like so:
public abstract class BaseRestClient
{
protected const string BaseAddress = "http://localhost:51009";
protected HttpClient Client;
protected virtual void ConfigureClient()
{
Client = new HttpClient { BaseAddress = new Uri(BaseAddress) };
Client.DefaultRequestHeaders.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
And called as follows:
var response = await Client.PostAsJsonAsync("api/Appt", model, cancellationToken);
Properties on response include:
StatusCode: 404`
ReasonPhrase: 'Not Found'`
RequestMessage:
{Method: POST, RequestUri: 'http://localhost:51009/api/Appt'`
This is the only request I'm making to a POST action, with a model parameter, but, ironically, a POST request to a GET action on an almost identical controller works fine. With the controller:
[System.Web.Mvc.RoutePrefix("api/Person")]
public class PersonController : BaseController
{
[HttpPost]
public async Task<IEnumerable<PersonDto>> Get()
{
Db.Configuration.LazyLoadingEnabled = false;
return await Db.Persons.ToListAsync();
}
}
the request made as below works fine and returns all my Person objects:
HttpResponseMessage response = await Client.PostAsync("api/Person", null, cancellationToken);
Two similar requests to GET actions also work perfectly.
So why would the one resource be found, and the other not? Are the any other, hidden reasons a 404 would be returned other than the requested resource not being found?
Could this be due to a conflict with two types of routing, e.g.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I have to use attribute routing in one place, the comment explains why:
[HttpPost]
[Route("Get")]
// NOTE HTTP 405 - Method not allowed when this action is named 'Get'.
public async Task<IEnumerable<BranchDto>> Fetch()
{
Db.Configuration.LazyLoadingEnabled = false;
return await Db.Branches.ToListAsync();
}
You are using the wrong route prefix attribute.
System.Web.Mvc.RoutePrefix("api/Appt")
is for MVC not Web API.
You need the one in System.Web.Http
System.Web.Http.RoutePrefix("api/Appt")
The reason the person controller example worked is because it defaulted back to convention-based routing.
original would have worked if you called POST http://localhost:51009/api/Appointment via convention-based routing.
A few weeks ago I decided to start building an API for my system which is fronted by an MVC portal. I built Web Api capability into my existing MVC site by adding:
class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
}
in my app_start folder, and modifying my Global.asax by adding:
GlobalConfiguration.Configure(WebApiConfig.Register);
It worked absolutely fine for calling simple methods in my Values controller either without the [Authorize] tag or in my browser by logging in first, but since then I've been reading around implementing basic authentication in asp.net web api and I've found a few examples I've tried to work into my implementation.
I have implemented a code example of a Message Handler I found online to authorize requests to it, at this stage simply comparing an ApiKey header string to one stored locally in my handler class to test it worked.
The handler looks like this:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
IEnumerable<string> apiKeyHeaderValues = null;
if (request.Headers.TryGetValues("ApiKey", out apiKeyHeaderValues))
{
var apiKeyHeaderValue = apiKeyHeaderValues.First();
// ... your authentication logic here ...
var username = (apiKeyHeaderValue == "12345" ? "Authorised" : "OtherUser");
var usernameClaim = new Claim(ClaimTypes.Name, username);
var identity = new ClaimsIdentity(new[] { usernameClaim }, "ApiKey");
var principal = new ClaimsPrincipal(identity);
Thread.CurrentPrincipal = principal;
}
return base.SendAsync(request, cancellationToken);
}
I then added it to my global.asax:
GlobalConfiguration.Configuration.MessageHandlers.Add(new ApiAuthHandler());
Now I took this code in it's entirety from here: https://dzone.com/articles/api-key-user-aspnet-web-api as I'm new to this and lots of implementations of authorisation seemed too complex for my needs/too complex for me to begin my learning with.
I do understand it's flow and from debugging it when receiving a request it does the ApiKey comparison correctly and creates the principle. The response however is not correct...and it never reaches the requested api method. I get this response:
Redirect
To:http://localhost:2242/Account/Login?ReturnUrl=%2Fapi%2Fvalues with status: 302 Show explanation HTTP/1.1 302 Found
It is redirecting me to my register method, as the [Authorize] tag is meant to, and it's actually returning my Register.cshtml in it's entirety. I can't figure out how to ignore this and let the ApiAuthHandler Authorize for me. I'm assuming I need to change something in the MVC pipeline somewhere but I'm not sure what.
Just want to get something very simple working so that I can get my head around it more to explore more complicated API authentication. Any ideas what I've done wrong?
Edit added api controller:
public class ValuesController : ApiController
{
ApplicationDbContext context = new ApplicationDbContext();
InboundDBContext inboundContext = new InboundDBContext();
// GET api/<controller>
[Authorize]
public string Get()
{
return user.Identity.Name;
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
Your WebApiConfig.cs file should look something like this...
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//add the authorization handler to the pipeline
config.MessageHandlers.Add(new new ApiAuthHandler()());
}
}