I have ProductsController and OwnersController:
public class ProductsController : ApiController
{
//constructor is here
// GET /api/products
public IHttpActionResult GetProducts()
{
return Ok(new ApiResponse());
}
// GET /api/products/{productCode}
[HttpGet, Route("api/products/{productCode}")]
public IHttpActionResult GetProductByCode(string productCode)
{
return Ok(new ApiResponse());
}
// POST /api/products
public IHttpActionResult PostProduct(Product product /*my class*/)
{
return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}
}
It works perfectly. But now I create second controller and do the same things but I get the error when trying POST method. Another methods work well!
Lets take a look at the code first:
public class OwnersController : ApiController
{
// constructor
// GET /api/owners/{label}
// GET /api/owners/{label}?skip=1&take=4
[Route("api/owners/{label}")]
public IHttpActionResult GetOwnersExamples(string label, int skip=0, int take=10)
{
return Ok(new ApiResponse());
}
// POST /api/owners/{productCode}
//[HttpPost]
[Route("api/owners/{productCode}"/*, Name = "CreateOwner"*/)]
public IHttpActionResult PostOwner(string productCode, Owner owner)
{
return CreatedAtRoute("DefaultApi", new { id = Owner.Id }, owner);
}
}
Error message:
UrlHelper.Link must not return null
My RouteConfig :
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
As I understand problem that CreateAtRoute
method must get another RouteName
. As you see I can solve the problem by adding Route Name parameter (commented now) and replace "DefaultApi"
with "CreateOwner"
but it looks like a hack. I believe there is another method to do it avoiding Name Property
.
PS Looks like my Web API can see only first controller (ProductsController) - any other methods doesn't work if I delete explicit Route [Route("...")]
...
As I understand problem that CreateAtRoute method must get another RouteName. As you see I can solve the problem by adding Route Name parameter (commented now) and replace "DefaultApi" with "CreateOwner" but it looks like a hack. I believe there is another method to do it avoiding Name Property.
Your understanding is almost correct. However you should specify a name not for the current route, but for the one that points to created resource. CreatedAtRoute
fills response Location header that should contain a GET-able URI for newly created resource.
Here is a working sample:
[HttpGet]
[Route("api/owners/{id}", Name = "GetOwnerById")]
public IHttpActionResult GetOwner(int id)
{
// Obtain the owner by id here
return Ok(new ApiResponse());
}
[HttpPost]
[Route("api/owners/{productCode}"/*, Name = "CreateOwner"*/)]
public IHttpActionResult PostOwner(string productCode, Owner owner)
{
return CreatedAtRoute("GetOwnerById", new { id = owner.Id }, owner);
}
(Note: For get this example working, you should comment GetOwnersExamples
action, otherwise multiple action will match your GET request.)
You said that it looks like a hack, but it is not. CreatedAtRoute
takes a route name as argument and you should provide it. How otherwise the proper action will be selected and Location
header will be built?
I solve the problem using next steps:
[RoutePrefix]
for controller - let them work by default - it works perfectly for simple requests. api/controller/{label}
and api/controller/{parameter}
- it can't understand which of them to use by default. (Or use explicit uri: api/controller?label=1
) All this actions let me delete excess attributes and make methods more readable.
Here is the result:
public IHttpActionResult PostOwner(OwnerWrapper modelWrapper)
{
string productCode = modelWrapper.Product.Code;
Owner owner = modelWrapper.Owners[0];
return CreatedAtRoute("DefaultApi", new { id = Owner.Id }, owner);
}
It is just test case, so we can see productCode
is never used, but my real realization is more difficult.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.