I'm currently doing this:
[HttpPut]
public void Edit(int id, Model model)
{
...
}
Which gives me the endpoint /api/controller?id=66 instead of what I want: /api/controller/66
To get what you want -- api/controller/66 on your PUT request, your HTTP verb attribute should be modified to [HttpPut("{id}")]
And the further reason why your id is obtained from the query string by default is that the parameter binding in the case of PUT request works in such a way that the primitive type is bound from request query string and the complex type from request body.
A brief of the parameter binding rules is listed out in this answer.
Related
I want to have an endpoint that looks like: localhost:5000/abc123
This is basically to replicate the functionality of tinyurl.
Controller
[HttpGet("/{myString}")]
public async Task<IActionResult> Get(string myString)
{}
This works but all files now come through this contoller eg: localhost:5000/runtime.js etc
Is this possible to do only for certain strings?
Use Route constraint to filter values for myString
For example, if a file name is a string containing a dot . is a valid suggestion in your case, you can use the following regex to accept alphanumeric strings
[HttpGet("/{myString::regex(^\\w+$)}")]
I have the following controller in ASP.NET WebApi 2:
[RoutePrefix("Validations")]
public partial class ValidationsController
{
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value)
{
var validator = new BankStateBranchValidator(DbContext.BankStateBranches);
var data = new ValidationsResult
{
IsValid = validator.IsValid(value ?? string.Empty)
};
data.Error = data.IsValid
? null
: "The BSB you have entered does not appear to be valid. Please check the value and try again.";
return Ok(data);
}
}
For historical reasons, the value parameter needs to be in the querystring, rather than the form body, which should be empty. So the expected API call would be POST /Validate/Bsb?value=012345.
That all works fine, and I get the expected result; however, sometimes we are getting clients calling the API with POST /Validate/Bsb or POST /Validate/Bsb?value=, and that is resulting in a 400 Bad Request response from WebAPI itself, because, as far as I can tell, the model binder is failing to bind the missing value to the parameter. If I put a breakpoint inside the method, it never gets hit.
So, given that I can't change the API contract, how can I handle this scenario? I've tried adding a [ValueProvider(typeof(RouteDataValueProviderFactory))] attribute to the parameter, and my test case for the missing value works, but then the valid value test cases break since the value isn't in the route but in the querystring.
Update
Based on Craig H's suggestion, I've added a default value to the value parameter. So the various scenarios are:
POST /Validate/Bsb?value=012345 - pass (valid value)
POST /Validate/Bsb?value=000000 - pass (invalid value)
POST /Validate/Bsb?value= - fail (empty value)
POST /Validate/Bsb - pass (missing value)
You should be able to make the parameter optional by specifying a default value in the method signature.
e.g.
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value = null)
Your question says that a query with ?value= was throwing a bad request.
When I tried this locally my breakpoint was hit and value was null.
If I omitted the QS parameter completely, then I received a method not allowed response.
This page makes mention of optional route parameters with attribute routing, although you are not specifying the parameter like that here.
I cannot find the document which describes the other options with regards to routing and optional parameters. I have seen one which indicates the differences between defining it as optional in the route definition, and optional in the method signature. If I find it, I will update this answer!
What would be the best way of receiving IEnumerable as a URI-parameter for a GET endpoint in .NET Core 3.x
In deeper context: What I want to do is filtering out data depending on a number of GUIDsĀ“.
So far I've got
[HttpGet]
public ActionResult Get(int allowedMissing, IEnumerable<Guid> ids)
.
.
.
Im calling the endpoint using Postman as such:
/endpoint?allowedMissing=2&ids=cab0cfcb-8eb2-4313-8064-e7b0841d4f8a&ids=b2b944c2-6b4a-4c32-9a2f-472b891e4843
This results in a 415 Unsupported Media Type without hitting any breakpoints inside the controller.
Ok, so default for receiving IEnumerable in an Action seems to be in body and will require decorating the parameter with FromQuery if sent as query.
Changing to this solved my problem:
[HttpGet]
public ActionResult Get(int allowedMissing, [FromQuery] IEnumerable<Guid> ids)
.
.
.
I have a REST interface endpoint like
POST /items/12345/actions
I utilize a generic actions sub collection to be apply to apply changes to 12345 which are not easily mapped to the content or direct other sub collections of it.
My question is the following: Since there could be multiple different action types I identify the action by a JSON property of the content of an uploaded document.
How do I select a action by a part of the JSON body of the request. Is there something possible like...
[Route("api/v1/items")
public class ItemsController : Controller
{
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionA")]
public ActionResult DoActionA(int id, ActionA a)
{
// do something
}
[HttpPost("{id}/actions")]
[CheckJsonBody("type", "ActionB")]
public ActionResult DoActionB(int id, ActionB b)
{
// do something
}
}
The request would look like ...
{
"type": "ActionA",
"abc": "xyz"
}
I have digged myself up into the code till Microsoft.AspNetCore.Mvc.ActionConstraints.ActionMethodSelectorAttribute (GitHub).
However starting from there, I am a bit lost to reach a high-performance solution. Do I need to decode the body or is that something which is already done at that time the constraint is evaluated?
ps: And yes, I know I could handle them in one action and do a switch on the "type" property.
An ASP.NET team member was so friendly to direct me to an answer: In the ActionMethodSelectorAttribute you can read the body into a memory stream, read till the property for the selection filter. Then you seek the memory stream to zero and replace it in the request (for later model binding). You can cache the criteria value in HttpContext.Items to speed it up if you use the same property for multiple actions.
I had an MVC controller with url which had a parameter binding with square braces in it like
public Product GetProduct([Bind(Prefix = "product[productid]") int id)
which used to work with the request url like
http://localhost:8080/Product/GetProduct?product[productid] = 1001.
I'm able to get the value for id as 1001 in MVC controller. Unfortunately I'm supposed to change this to an web api controller and I have the api controller method defined like this
public Task<Product> GetProduct([FromUri(Name=product[productid])] int id)
for which i get a 404 response with the same request url . The [FromUrl] works with the parameter name without braces when
defined like
public Task<Product> GetProduct([FromUri(Name=productid)] int id)
and the url like
http://localhost:8080/Product/GetProduct?productid = 1001
Is there any workaround for this apart from parsing the request url with braces and get the value.
Your URL is right. You should fix the controller the following way:
public Task<Product> GetProduct([FromUri(Name=product.productid)] int id)
I don't really know why this works.