문제

I have an MVC Web Api RESTful service. I am not using API version 2.

I created a Controller with the default GET/POST/PUT/DELETE. Everything was fine until I decided to add some more functionality to my controller.

Long story short, I ended up with near a dozen route templates (very nasty WebApiConfig file) for my controller. It was working fine until.... i needed a second controller. Then the chaos began.

All the routes for the DocumentController obviously didn't work for CompanyController because what I had previously configured was just a bunch of crappy patches to make my first Controller work. Here I am four days after and still can't get it to work. I truly can't grasp my head around this whole routing thing. So complicated and inflexible (in my opinion).

Here is some code for you to understand.

public class DocumentController : ApiController
{
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse(HttpStatusCode.OK,"This is GET");
    }

    [HttpGet]
    public HttpResponseMessage Metadata()
    {
        return Request.CreateResponse(HttpStatusCode.OK,"This is Metadata");
    }

    public HttpResponseMessage Get(int id)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is GET with ID");
    }

    public HttpResponseMessage Post([FromBody] DocumentDTO document)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is Post");
    }

    public HttpResponseMessage Put(int id, [FromBody] DocumentDTO document)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is PUT");
    }

    [HttpGet]
    public HttpResponseMessage Tags(int id)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is GET TAGS");
    }

    [HttpPost]
    public HttpResponseMessage Tags(int id, [FromBody] IEnumerable<TagDefinitionDTO> tagDefinitions)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is POST TAGS");
    }
}

So, my initial dream was to achieve the following

GET /document/ => ALL DOCUMENTS

GET /document/3 => Document Id=3

POST /document/ => CREATE A DOCUMENT

PUT /document/3 => EDIT A DOCUMENT

GET /document/metadata => RETURNS GENERAL CONFIGURATION FOR ALL DOCS

GET /document/6/tags => GETS ALL TAGS FOR DOCUMENT ID=6

POST /document/6/tags => CREATES TAGS FOR DOCUMENT ID=6

I don't even post the WebApi because it's quite embarrassing. Like I said, I was able to make it work with the extra methods but it was almost a one to one mapping to my controller/actions. AWFUL... I know.

When I added a second Controller.... obviously... Mission Impossible.

Now, don't want you to think I haven't done some research (i've used constraints, defaults, etc., etc., to not avail... pitiful). Once I get one Controller to work, the other breaks.

I get all sorts of errors. From "more than one method for GET" to "Resource Not Found", "method now allowed POST".

Here I am, five days later going in circles. This is extremely frustrating.

QUESTION.

Has anyone faced these issues? Is the routing deal SO limiting that you can only have those 4 operations per control? Should I create a route template PER action in my controller, meaning that if I have 10 controllers with 10 actions I should add 100 route templates?

도움이 되었습니까?

해결책

Building on the answer from Yishai, I think changing your routes to the following should work for your scenario:

config.Routes.MapHttpRoute(
    name: "DefaultApi1",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional, action = "Default" },
    constraints: new { id = @"\d*" }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi2",
    routeTemplate: "api/{controller}/{action}",
    defaults: null,
    constraints: new { action = @"[a-zA-Z]+" }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi3",
    routeTemplate: "api/{controller}/{id}/{action}",
    defaults: null,
    constraints: new { id = @"\d+", action = @"[a-zA-Z]+" }
);

Then on your default actions place an ActionName attribute.

public class DocumentController : ApiController
{
    [ActionName("Default")]
    [HttpGet]
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse(HttpStatusCode.OK,"This is GET");
    }

    [HttpGet]
    public HttpResponseMessage Metadata()
    {
        return Request.CreateResponse(HttpStatusCode.OK,"This is Metadata");
    }

    [ActionName("Default")]
    [HttpGet]
    public HttpResponseMessage Get(int id)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is GET with ID");
    }

    [ActionName("Default")]
    [HttpPost]
    public HttpResponseMessage Post([FromBody] DocumentDTO document)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is Post");
    }

    [ActionName("Default")]
    [HttpPut]
    public HttpResponseMessage Put(int id, [FromBody] DocumentDTO document)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is PUT");
    }

    [HttpGet]
    public HttpResponseMessage Tags(int id)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is GET TAGS");
    }

    [HttpPost]
    public HttpResponseMessage Tags(int id, [FromBody] IEnumerable<TagDefinitionDTO> tagDefinitions)
    {
        return Request.CreateResponse(HttpStatusCode.OK, "This is POST TAGS");
    }
}

As mentioned, Web API 2 is a much better way to go.

다른 팁

Yes you can, switch to using attribute routing.

Then on each action or controller you can just put the route template.

OR the following routes should work for you

config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
    name: "DefaultApi1",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { id = @"\d+" }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi2",
    routeTemplate: "api/{controller}/{id}/{action}",
    defaults: null,
    constraints: new { id = @"\d+" }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi3",
    routeTemplate: "api/{controller}/{action}",
    defaults: new {id = RouteParameter.Optional}
);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top