I am calling my WebApi server methods from a console client and most of the calls work.
Some don't.
My routing seems fine but obviously there is a bug.
Is there a way to set a breakpoint somewhere ( earlier than my controller method ) which will allow me to trace the source of the error.
You must have two methods in the controller that is being called that are 'identical' to the routing engine. eg
public class FooController
{
public void Get1() { }
public void Get2() { }
}
if you attempt to call somedomain.com/api/foo you will get a 500 server error
Multiple actions were found that match the request: \r\nSystem.Net.Http.HttpResponseMessage
Remember that in WebApi (by default) there is no {action} part of the route:
{controller}/{id}
meaning the API has a convention for which method is called.
See this article explaining more
Related
NotSupportedException: Ambiguous HTTP method for action - AAON.Insiite.Api.Controllers.TokenController.Get (AAON.Insiite.Api). Actions require an explicit HttpMethod binding for Swagger 2.0
I've read the several questions regarding this issue on SO but none of the solutions have worked.
I've tried:
Adding [HttpGet] to the method in question.
Adding [NonAction] to the method in question.
Changing access modifier of method to Protected.
Deleting the method still throws an exception for the now deleted method.
Cleared Visual Studio Cache
I've tried clearing my visual studio cache but to no avail.
[HttpGet]
public IActionResult Get()
{
return Ok("Running");
}
According to #Crumblenautjs answer, I had a similar problem with the custom routing convention. After debugging for a while I discovered that also RouteAnalyzer_Main (which is used by swagger to define routes) routes are being replaced together with "my" controllers.
My solution is to skip Apply when RouteAnalyzer occurs, at least works for my solution.
public void Apply(ControllerModel controller)
{
if (controller.ControllerName == "RouteAnalyzer_Main")
{
return;
}
}
The issue was a controller naming convention. Solved.
I would like avoid having to call
AreaRegistration.RegisterAllAreas()
in my global.asax, because I'm trying to move all startup logic into individual classes inside of the App_Start folder. However, I've been unsuccessful in getting this to work. The first option attempted used code like this:
[assembly: PreApplicationStartMethod(typeof(Startup), "PreInit")]
namespace Foo
{
public class Startup {}
}
Where PreApplicationStartMethod is coming from the System.Web namespace. In this case, the call to register areas occurs too early.
The second approach, based on this post by David Ebbo, uses WebActivator:
using System.Web.Mvc;
[assembly: WebActivatorEx.PostApplicationStartMethod
(typeof(AreaDemo.AreaConfig), "RegisterAreas")]
namespace AreaDemo
{
public class AreaConfig
{
public static void RegisterAreas()
{
AreaRegistration.RegisterAllAreas();
}
}
}
Unfortunately, although there is no error thrown, attempting to navigate to the area fails (as if registration never occurred).
What is the proper way to register areas in ASP.NET MVC 5 from a startup class using an assembly directive rather than a direct call from Global.asax?
Update 1: Here is my AreaRegistration code:
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "AreaDemo.Areas.Admin.Controllers" }
);
}
}
For some reason the defaults are ignored, but navigating to /admin/home/index/0 works. /admin, /admin/home, and /admin/home/index all 404.
I believe this to be an ordering issue (which it seems you suspect as well). I think that the areas are getting registered after the Application_Start event, and thus the areas' routes are registered after the non-area routes.
The reason that the 4-segment URL (/admin/home/index/123) works is that it can't match the "default" route of the MVC app. So, that default route gets skipped (because it matches only 1-, 2-, and 3-segment URLs), and routing will find the area-specific route that can handle a 4-segment URL. A 1-, 2-, or 3-segment URL will match the non-area route, but of course there are no controllers outside of the areas to handle such a URL, and thus the 404 is returned.
If I understood correctly, you want the areas to be registered after Application_Start, but before anything "else" happens? Unfortunately I'm not aware of any specific event to handle that. From an IHttpModule you could perhaps try handling an event such as BeginRequest, which happens super early, quickly do the registration there only once (that is, don't register stuff on every request!), and that ought to allow you to sneak in before ASP.NET Routing does its stuff (which happens a bit later, during PostResolveRequestCache).
A complete alternative to this is to use attribute routes, which many people like because it can help avoid ordering problems.
Grails makes it very easy for a Controller to call into a Service and for a Controller to forward a request onto another Controller.
So suppose you have a service method as such
List<String> updateNames() {
...
}
You can call it very easily from any controller.
I am wondering, say if you have an edge case where you realise there is a validation problem in your service method. You don't want to throw an Exception back to your controller, because it is not really an exceptional case. But you can't return back an error message from your Service to the Controller that called because that will mean you have to use some wrapper object instead of a nice List
Is there anyway for these cases, you can get the Service to do a server side forward onto another Controller which could return an Error response to user?
Thanks.
Grails already have a structure for validation in your beans, called Errors (comming form Spring). For example, if you have a service to upload files, you could easily attach validation errors in your bean:
class UploadService {
void doUpload(MultipartFile file, MyDomainClass domainClassInstance) {
if(validationsFail) {
domainClassInstance.errors.rejectValue("myUploadField","my.i18n.code")
}
}
}
If it's not a domain class, you can consider using a command object since they're validateable too.
In your controller, it's just a metter of checking if your instance has errors:
def upload() {
MyDomainClass instance = ...
uploadService.doUpload(request.getFile('file'), instance)
if(!instance.hasErrors()) {
//save and go on...
}
}
Another option is to work with exceptions like #Joshua Moore answered. Just remember to extend RuntimeException. If you don't, your transaction will not be rolledback automatically.
Services are not aware of the web/http request context in this way. I won't get into how this line is blurred with session or request scoped services since it still doesn't apply to what you are asking about. Plus, you really don't want your service to even be aware that it's dealing with a web/http request, since you want to separate responsibilities and have a good/clean design.
So, back to your question. This is exactly the case for raising an exception from your service and letting your controller handle the result of that exception. If it's a validation error on an instance then you should be able to access the errors collection of the instance in your controller (provided of course that it was an input into your service).
As a side note about exceptions from services. Stack traces are expensive to fill in. This is even more so in Grails since there are a lot of pieces at work. I highly recommend if you are going to raise your own business logic exceptions from your services that you override the fillInStackTrace method on your exception to avoid this cost.
Here is an example:
package com.example
class MyBusinessException extends RuntimeException {
List<String> argList = []
public MyBusinessException (String message, List<String> args){
super(message)
argList = args
}
public MyBusinessException (String message){
super(message)
}
/**
* Don't fill in the stack trace because we want things to be faster.
**/
#Override
public Throwable fillInStackTrace() {
// do nothing
return this
}
}
Is there a way to handle 404 (resource not found) generically, or do I have to write logic for each action? A simple example of what I am currently doing:
//single-read
public HttpResponseMessage Get(Guid guid)
{
School school = new School(guid);
if (school == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
//bonus-question: would it be better to instead: throw new HttpResponseException(HttpStatusCode.NotFound);
}
//bonus-bonus-question: what is the benefit of using a typed response?
return Request.CreateResponse<School>(HttpStatusCode.OK, school);
}
If possible, I would like the "not found" logic to be handled elsewhere so that I didn't need to repeat those few lines of checking for each action. For example, I'd like it if it could be reduced to:
//single-read
public HttpResponseMessage Get(Guid guid)
{
School school = new School(guid);
return Request.CreateResponse<School>(HttpStatusCode.OK, school);
}
You can implement an IActionFilter on your controller which will be called everytime an action is about to be executed and also when an action has finished execution. You can then perform your checking logic there. See documentation. You would annotate the controller class itself with your filter and it would be called for all actions.
In terms of the error handling, if you don't throw an exception, then you won't pay the cost of exceptions (which I'd like to pretend to be negligible), but more importantly, you won't allow any exception handlers to kick in and that may be something you actually want. Take a look at IExceptionFilter for example that gets called whenever an exception is thrown. If there is a part of your application that relies on that (for example logging errors), that component won't know that there was an error when you don't throw an exception, so it's a design call.
I have some issues with getting the RescueAttribute in Caliburn 1.1.0 to work. Basically I intend to put a general catch-all exception handler in every view model, but I it seems like the method never gets called.
[PerRequest("ConfigurationGroupViewModel")]
[Rescue("GeneralRescue")]
public class ConfigurationGroupViewModel : Presenter
{
................
public void GeneralRescue(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I have followed exactly what was specified in the Caliburn documentation, and I saw a post earlier that mentioned something about IConventionManager but my concern is that even by putting the rescue method directly on the ViewModel class itself the rescue method never gets called. Can anyone assist me in this?
Thanks!
Could you please provide some other details?
Are you invoking action with messaging infrastructure or manually? (Rescue only works in the first case)
Are you calling a regular action or a coroutine (IEnumerable)?