I'm writing an api controller in ASP 5. I want to return a bad request code exception if the parameters passed to the service are incorrect. In the current version of webapi I would do:
throw new HttpResponseException(HttpStatusCode.BadRequest);
However HttpResponseException is part of System.Web, which has been removed from ASP 5 and thus I cannot instantiate it anymore.
What is the proper way to do this in vNext?
To answer your question, you can use the WebApiCompatibilityShim which ports HttpResponseException (and many other features) forward into MVC 6. (thanks to DWright for the link to that article.)
It seems like the MVC-6 way to do it is to return an IActionResponse from your method and then call HttpBadRequest() which is a method on the base Microsoft.AspNet.Mvc.Controller class that returns an BadRequestResult, which I believe is the best way to get a 400 status into the response.
The Controller class has methods for other response codes as well - including the HttpNotFound() method which causes a 404 to be returned.
See also: https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api
Related
I am building a WASM app for the first time, and have been following tutorials.
The Solution I have is composed of 3 projects created by the wizard (Client, Server and Shared).
I am having trouble when making the following request from the index page:
var msg = await Http.PostAsJsonAsync<u001_000_001>("api/u001_000_001", userRec);
If (msg.IsSuccessStatusCode) ClearUserScr();
In the Server project, I have a Controllers folder with a controller named u001-000-001Controller (although the class name in the file is u001_000_001Controller). The relevant lines of code from the controller class are as follows:
[ApiController]
[Route("api/[controller]")]
public class u001_000_001Controller : ControllerBase
{
[HttpPost]
public async Task<u001_000_001> Post([FromBody] u001_000_001 create)
{
EntityEntry<u001_000_001> user = await db.u001_000_001.AddAsync(create);
await db.SaveChangesAsync();
return user.Entity;
}
}
The HttpClient is registered using the builder.HostEnvironment.baseAddress as the Uri in the Client Program.cs file.
The Shared folder contains the handler called u001-000-001 (class name u001_000_001).
I have tried all the different combinations I can think of in terms of changing the names in the actual call, and nothing works. I keep getting the same "not found - HTTP 400' error.
I would sincerely appreciate help from experienced eyes to see if there is a simple mistake I'm making or if there's something more serious I'm missing. Many thanks in advance for your time.
Many hours of research later, the error was found to be in the fields being fed initially into the handler, rather than anything happening with the actual HttpClient request or the controller.
Although I found that the Http/1.1 400 Bad request error could be generated by a range of issues, I would highly recommend reviewing the structure of the data being input as a first step, since this was overlooked in my case.
Clarification of Issue and Solution:
I have a process for creating new user logins, and the goal of the HttpClient PostAsJsonAsync request was to send new account details to the database. However one of the fields in the user record is a PIN number, and as this is not chosen by the new user in the first registration step, it was left as null in the code.
Keeping it null was fine for the code, but the Controller expects data to be input for all fields, and will not accept PostAsJsonAsync calls where any of the fields are left null.
The solution in my case was to set a temporary value, and then make the PostAsJsonAsync request with all of the fields filled and sent through the request.
I wish to thank the professionals who commented with potential solutions, as they helped to improve my code.
I'm learning about using problem details middleware found here
I have the setup working all fine but I got curious why it's mapping validation errors differently than the default status code.
To explain better, in the sample repo provided by the owner try the following:
call https://localhost:54547/mvc/modelstate
response "status":422
In the project's Program.cs, comment out the MVC override AddProblemDetailsConventions (line 46) and call again
response "status":400
400 is the default status code for validation errors automatically inserted when you add the ApiController attribute to your controller.
In a previous discussion with the owner here, it was recommended to call AddProblemDetailsConventions
if you want to have 100% consistent error responses from your API (produced by the middleware).
I understand the middleware is to control the "format" of response error message to follow RFC7870, but why is it changing the error code for this example case? is 422 more specific/better practice than 400?
I tried to look for more details, but couldn't find any. like what other mappings are changed, or if there's a way to configure the middleware mapping for default validation error (since in our project we already have test suit asserting on 400 for validation scenarios).
From that same conversation with the author you cited, he does mention a way to override the default status response code in this post.
Regarding the 422 status code; it's an opinion of mine that syntactically correct, but semantically invalid requests should return back a different status code than 400 Bad Request
He also mentions that not everyone may choose to follow that convention, so he provides a way to override the default:
Some people don't like it (often because it's part of the WebDAV RFC and not an "official" HTTP RFC (but this will soon change, with the inclusion of 422 in HTTPbis' upcoming HTTP Semantics RFC, which obsoletes RFC 7231), so I've added an option to change it:
And provides a link to the source code value of ProblemDetailsOptions.ValidationProblemStatusCode.
You can pass in the options value to the configuration like this to change the default back to a 400 status code:
services.AddProblemDetails(options =>
{
options.ValidationProblemStatusCode = 400;
});
Or if you prefer to use the private configuration method like in the sample library:
private void ConfigureProblemDetails(ProblemDetailsOptions options)
{
options.ValidationProblemStatusCode = 400;
// the rest of the code setup he used in the example
}
As far as the other mappings that were changed, I don't see much in the source code that is configured by default apart from setting the status code to 500 if there is no status code present.
I a newbie to webapi and have created a web api project. Different controller method here needs image as parameter. I am using an external 3rd party api to check if the image uploaded by the users is not any profane image. So instead of checking it at actionMethod level ,i thought it might be a good idea to check using a filter that way it will save me time of checking it individually. But i haven't got a clue as to how to start writing the code for this.
public class ImageFilter : FilterAttribute,IFilter
{
public void OnActionExecuting(HttpActionContext httpActionContex)
{
if(!httpActionContex.ActionDescriptor.) // ???? what should come
}
}
please guide me. Don't need the exact code just the correct direction and guidance .. thanks
A FilterAttribute is, as its name implies, an attribute that can be set globally on the WebAPI pipeline, or individually on a specific controller method. You can simply slap the [ImageFilter] attribute on your specific controller method, and the WebAPI pipeline will execute the filter before executing the action method - giving you a chance to filter what requests make it to the method.
For the actual implementation of your custom logic, you can access the HttpContext.Current.Request in your OnActionExecuting method, allowing you to access the incoming HTTP request. You can then read the data from it, pass it to your 3rd party API, and if it doesn't pass the filter, you can access the Response and end it before it even reaches the controller:
var response = HttpContext.Current.Response;
response.StatusCode = (int)HttpStatusCode.BadRequest; // or whatever
response.End();
I've got an ASP.NET Web API project that I'm working on. I've got an APIController called SpellbookController that has basic CRUD operations to an EntityFramework repository.
However, when I try to add a method that takes a parameter that's not an id, I can't seem to get it to route correctly.
Here's what it looks like:
// GET: api/Spellbooks/user#email.com
[ResponseType(typeof(List<Spellbook>))]
[Route("api/Spellbooks/{username}")]
public IHttpActionResult GetSpellbook(string username)
{
List<Spellbook> spellbooks = db.Spellbooks.Where(x=>x.Username == username).ToList();
if (spellbooks == null)
{
return NotFound();
}
return Ok(spellbooks);
}
So I'm trying to hit http://localhost:xxxx/api/Spellbooks/emailaddress,
but I get a 404 every time.
It's definitely an APIController, and I have config.MapHttpAttributeRoutes(); turned on.
What am I missing?
Where is your username parameter?
Your call should looks like this-
http://localhost:xxxx/api/Spellbooks/emailaddress/David
Update
Try to declare the parameter as string {username:string}
Update 2
The problem is with your '.' as you can see in this link.
You can send the email without the point in the parameter and then replace ite back to you regular mode or you can use all the good advice that the link provide.
If you step through it, do you get a value when looking at username? Or is it always null?
If your route is not working, a simple way to debug the route is to make a request to http://localhost:xxxx/api/Spellbooks/?emailaddress=thisemailaddress and see if you can get a response. In fact, from a REST standard, it can be argues that it's a cleaner route, since you will be returning a collection of elements, rather than a single object.
I am having a problem with silently failing deserialization in ASP.NET Web API (version 5.1.2). I would like the deserialization to raise an error instead but I am unable to find a configuration for it.
My specific (simplified) case is this. A client application (AngularJS) sends a HTTP POST request to the ASP.NET Web API backend. As a payload there are a bunch of strings:
["ABC100", "ABC200", "ABC300"]
However, the server is expecting a list of integers:
List<int> Ids { get; set; }
What ends up happening is that the deserialization fails, the Ids list will be empty and there are no errors.
Ids: []
Of course the mismatch needs to be fixed as well, but it seems obvious to me that the POST request should fail in this case. How can I make it the default?
One solution to this problem seems to be checking the ModelState.IsValid property right at the start of the controller method:
[HttpPost]
[Route("Stuff/Ids/")]
public void PostStuff(List<int> Ids)
{
if(!ModelState.IsValid)
throw new Exception("ModelState is not valid.");
// Carry on...
}
The ModelState.IsValid is indeed false in the case described by my question.
The check can be made global by creating an action filter out of it. Instructions for this can be found for example in this article: Model Validation in ASP.NET Web API