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.
Related
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 need to figure out from a middleware if a route or context points to a valid endpoint in my API. I want to do this in order to send a valid json-formatted error response, instead of the default empty error message that the API sends.
An alternative solution that figures out that the endpoint resulted in nothing is fine too. My first thought was to use a middleware, but perhaps sending an error with a fall-back controller works too?
I would like to give an answer to my own question, as I have found a way to manually check if a route exists. This was something I did not think of at the time, as I did not realise you could get information about your API through a dependency.
The way I have done this now is to make use of the IActionDescriptorCollectionProvider provider. This will allow me to receive all current routes in the API. Using this, I created the following middleware:
public async Task InvokeAsync(HttpContext context)
{
var path = context.Request.Path.Value;
var routes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.Select(ad => $"/{ad.AttributeRouteInfo.Template}").ToList();
if (!routes.Any(route => path.Equals(route, StringComparison.InvariantCultureIgnoreCase))) {
context = await context.HandleRequest(HttpStatusCode.NotFound, "RouteNotFound", "De server heeft geen geldige actie voor de gegeven route.");
return;
}
await _next(context);
}
This fully allows me to respond with a custom error (this is using HandleRequest(), which is an extension of my own), and handle the rest in the frontend.
I found another way to solve this to use pre-initialised documentation by the API. I'm not sure what to call it, but adding the following code to your csproj file creates an XML which gives the same benefits:
<NoWarn>$(NoWarn);1591</NoWarn>
<DocumentationFile>Files\Documentation\$(Configuration)_$(AssemblyName)_doc.xml</DocumentationFile>
This means that the XML has be parsed of course.
I am still looking for different solutions, perhaps better ones if there are problems with this one.
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 currently have my solution set up to produce Swagger documentation for each end point. However I have several end points that are only available for admins. Down below you will be able to see an example.
A regular user can create models, however only an admin can pull every single model in the database.
The challenge is to generate 2 sets of swagger documentation? One for regular users to see, and another piece of documentation for Admin users to see. I know that if I add [ApiExplorerSettings(IgnoreApi = true)] to my end point it will not appear in the documentation generated however this would mean that my admin users wont be able to see that vital piece of documentation as well. Any recommendation on how to dynamically generate two sets of documents depending on the user will help.
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<PackageResponse>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[HttpPost("/v1/packages")]
[Authorize()]
public async Task<IActionResult> CreateModel([FromBody]Request request)
{
...
}
The method below is for admins only:
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<PackageResponse>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[ApiExplorerSettings(IgnoreApi = true)]
[HttpPost("/v1/packages")]
[Authorize()]
public async Task<IActionResult> GetAllModelsFromDatabase([FromBody]Request request)
{
...
}
A dynamic process was found in this answer.
Dynamically Ignore WebAPI method on controller for api explorer documentation
It is possible to separate swagger documents however there is no built in method to do this. One would have to remove the un wanted nodes from the one documentation file:
https://github.com/swagger-api/swagger-editor/issues/233
This works fine in current editor if you host the editor yourself so
parameters_common.yaml path can get resolved as an HTTP path.
Currently there is no way to jump between files or create a new one.
If you are doing a big project with Swagger, I recommend hosting the
editor yourself. When editor and the API you are building are on the
same origin, XHR call don't have to be cross-origin which help editor
to show more details about calls in "try-operation" and your API
doesn't have to have cross origin headers.
Example on how to split swagger file into smaller nodes.
http://azimi.me/2015/07/16/split-swagger-into-smaller-files.html
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