I'm having a problem testing routes and I think it's due to the route using a custom handler.
This question points to a similar issue, but doesn't quite fix what I'm doing.
I have a RouteConfiguration class that simply houses the route definitions to enable me to pull them in for the application as well as any testing routines:
// the route
ApiRoutes.MapHttpRoute(
name: "Custom",
routeTemplate: "api/service/{service}/{method}/{arguments}",
defaults: new { arguments = RouteParameter.Optional },
constraints: null,
handler: new ServiceDispatcher(_httpConfiguration, _controllerHelper)
);
Now when trying to test this using a similar pattern to this way of mocking the route it doesn't find the route (404). The select part looks like this (other code in this article is not shown):
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/service/foo/bar/foobar");
DefaultHttpControllerSelector controllerSelector = new DefaultHttpControllerSelector(configuration);
HttpControllerDescriptor descriptor = controllerSelector.SelectController(message);
My (educated?) guess is this DefaultHttpControllerSelector but I don't know how or if it can be changed to support what I'm doing.
So, can I make this current routine work using custom handlers or am I barking up the wrong tree?
When you have a MessageHandler attached to the route, the controller dispatcher is never even called. It doesn't get that far.
If you want to test the routes, I would just call
var routeDate = config.Routes.GetRouteData(request);
Then you can check the route values and test for the service, method and arguments parameters.
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?
I've got Swashbuckle going in my web api project. I want to get a custom handler invoked only when a user tries to hit the swagger ui page. I don't want to add the handler to the pipeline.
I thought something like this might work but it doesn't:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "SwaggerUI",
routeTemplate: "docsite/{*assetPath}",
defaults: null,
constraints: null,
handler: new MyHandler() { InnerHandler = new HttpControllerDispatcher(config) }
I use route attributes everywhere else in the project so I map those attributes first.
My swagger config is using
.EnableSwaggerUi("docsite/{*assetPath}",c =>
hence the docsite route.
I don't want to add the handler to the pipeline because I don't want it to get called with every web api call. I'm using it to restrict access to the swagger UI.
Also I'm using OWIN to authenticate one particular api so I get this error when I try to call that api
The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'MyHandler' is not null.
Parameter name: handlers
Figured I can avoid the error by just making sure the handler only happens when I do a swagger ui route.
Who wants to help!
I am new to C#, committing to a little Spike. I have built a few POCO OWIN self-hosting services and am trying to create a controller but guides and documentation everywhere appear to be pointing me in the incorrect direction.
I am adding default mapping to a configuration method inside a startup class. Then in my controller I have a simple GET method. This works like a charm, when I send a request to it using the url defined in my startup routing the method gets invoked. But now I need to set up a second method to be invoked by a new url.
I don't understand what I am missing and what I am not understanding but everything I attempt does not work for me:
relevant code in the controller:
// GET api/ManagedService
public string[] Get()
{
Start();
return new string[]
{
"Job Processed."
};
}
Relevant code in my startup class:
HttpConfiguration config = new HttpConfiguration();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/start"
);
So on visiting api/{controller}/start I invoke my get but I want to invoke a different method with api/{controller}/receiptandsend.
I'm not 100% sure I understand what the problem is but try to change your routeTemplate to "api/{controller}/{action}" instead. The routeTemplate you are using assumes that you have an action (method) called start on every controller. Alternatively you could try to add another route, something like name: "anotherOne", routeTemplate: "api/{controller}/receiptandsend" but I don't think that is really the way to do it correctly.
edit: By the way, your Get method should probably be named something else, like StartJob or something, "Get" seems a bit misleading seeing that you don't get anything back except a text saying that you started some other process.
I am facing an issue currently with using ASP.NET Web API for the NopCommerce website which I need to connect to my cross-platform application.
The code breaks when I call the API. It produces an exception at:
var scope = EngineContext.Current.ContainerManager.Scope()
In the task.cs page.
I have tried debugging the code, but the exception is not generated regularly; sometimes it comes and sometimes not. I did, however, find some points where it breaks:
var sis = EngineContext.Current.Resolve<StoreInformationSettings>().
I have tried loading it till it loads. i.e., putting it inside a while statement as:
var sis = EngineContext.Current.Resolve<StoreInformationSettings>()`
while(sis == null)
{
sis = EngineContext.Current.Resolve<StoreInformationSettings>()
}
And similarly, a few more instances. This implies it loads, but after a delay, maybe.
There was an instruction in the nopcommerece forum to update the autofac package, which I did.
All the Autofac Nugets are tried individually in Nop.Web.
With reference to this link here, I have tried fixing, but it didn't solve my problem.
I came to the conclusion that it has some issue with the Autofac DI settings (not sure). Or is it that NopCommerce is built not to support APIs?
As Mr. Rigueira suggested, I have tried to register the route from the route configuration class inside my plugin itself.
The route configuration for the API is here:
public void RegisterRoutes(RouteCollection routes)
{
GlobalConfiguration.Configure(config =>
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { controller = "TestApi", id = RouteParameter.Optional }
);
});
}
Still, it does not seem to make any difference.
I tried registering the route from the Plugin in the normal pattern:
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"WebApi",
"api/{controller}/{action}/{id}",
new { controller = "WebApi", action = "", id = RouteParameter.Optional },
new[] { "Nop.Plugin.WebApi.EduAwake.Controllers" }
);
}
Still no go.
here's the error i get :
Multiple types were found that match the controller named 'Topic'. This can happen if the route that services this request ('api/{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
Line 13: <div class="col-md-12 padding-0" id="home-topic" style="margin-left:-14px;">
Line 14: <div id="home-topic-content" style="width:104%;">
Line 15: #Html.Action("TopicBlock", "Topic", new { systemName = "HomePageText" })
Line 16: </div>
Line 17: </div>
in index.cshtml.
this seems to be a hopeless condition.
I have been working on this since months!
Checking your code would be advisable but, most likely, you have by-passed nopcommerce when registering Web API, and now you're trying to use dependencies before they have been registered in the usual flow. That's why they eventually appear while you're waiting in the loop (I can't imagine how you have found such a workaround anyway...).
As a solution, you should integrate Web API using a plugin and not by modifying the original source code. I did it in the past, and it is not hard. There used to be a couple of samples for that if I don't recall badly.
So, we're developing a webapi-heavy application that serves multiple types of clients (including a website that's a JavaScript client and multiple C# clients), and we'd like to use attribute routing to make friendlier urls. Something like this:
[RoutePrefix(Constants.RoutePrefixes.Api + "foo")]
[HttpPut]
FooController : ApiController
{
[Route("{fooId}/sub/{subFooId}")]
[HttpPut]
public HttpResponseMessage UpdateAFoo(int fooId, int subFooId)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
}
And a request to /api/foo/1/sub/2 would hit this controller, as expected.
However, we have a message handler built into one of our default routes for processing identity tokens, and requests coming from clients that require that special behavior still have to use that route, or at least something that runs the appropriate message handler, and the following request (which uses that other route):
/integration/foo?fooId=1&subFooId=2
..gives me an error that no suitable controller could be found to handle the request. If I remove the route attribute, this second request hits my action method as expected.
I've read in several places that you can use both default and attribute routes, and I have both working in the application, but I haven't been able to use both for a specific action.
My route config looks like this:
public static void RegisterHttpRoutes(HttpConfiguration configuration)
{
var routes = configuration.Routes;
configuration.MapHttpAttributeRoutes();
routes.MapHttpRoute(
name: Constants.RouteNames.Api,
routeTemplate: Constants.RoutePrefixes.Api + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: Constants.RouteNames.IntegrationApi,
routeTemplate: Constants.RoutePrefixes.IntegrationApi + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional },
constraints: null,
handler: new SuperDuperAuthenticationHandler())
);
}
Is that just how it works? Do I need a separate action without the route attribute? Is there a way to specify message handlers for attribute routes? Can I have both attribute and convention routing apply to the same action method? I'm not really clear on what my options are.
Controllers/Actions which are decorated with attribute routes cannot be reached via routes matched by conventional routing...so the behavior that you are seeing is expected...
Per-route message handlers are not supported for attribute routes.
Can you use AuthenticationFilterAttribute for your scenario?...if you need this filter for a set of controllers, then you could probably create a base controller decorated with this filter and let all these set of controller derive from it...