How to cancel webapi bulit in parse response - c#

first its something that happening in webapi 2.2 and not in the old one
https://devblogs.microsoft.com/aspnet/asp-net-core-2-1-web-apis/
I get from webapi response
{
"ProductValue": [
"The input was not valid."
]
}
how i cancel this response and just get false in
ModelState.IsValid
i need to return more fields to response
and this response is not good for me
for those who have hard trouble to understand in dubug i dont enter to
this function at all,because web api built in mechanism
return his response instend of mine
{code=9}
public MyResponse Start(Request req)
{
if (ModelState.IsValid)
{
return new MyResponse(){code=0} ;
}
return new MyResponse(){code=9} ;
}

Not absolutely sure what you are trying to achieve but if you want to send your own custom error response then you can probably do something like below (Hypothetically)
Product p = GetProduct(productvalue);
if (p == null)
{
HttpError err = new HttpError($"Product with productvalue {productvalue} not found");
return Request.CreateResponse(HttpStatusCode.NotFound, err);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, p);
}

services.AddMvc()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
thanks to Kirk Larkin

Related

API Record not found

I build this API that takes in Id if that Id is found It will return the data. But if that Id is not found I return 409. When I test it in postman I see the status as 409 is that correct? is that all I need or should it also return some text in the body?
[HttpGet]
public IHttpActionResult Read(string type, string id)
{
if (id== null)
{
var msg = new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = "Unable to Find Id" };
msg.Content= new StringContent($"No entity with {id} was found");
return ResponseMessage(msg);
}
}
You do see a "not found" text:
You don't see anything in the body, because your API doesn't send a body, just a HTTP header
Re your comment, and linking in GPW's advice, return something custom - let errors be errors, and have this foreseeable condition as an "OK but no" response, perhaps:
[HttpGet]
public ActionResult Read(string type, string id)
{
if (id == null)
return Json(new { status= "fail", message= "id parameter is required" });
else if (type == null)
return Json(new { status= "fail", message= "type parameter is required" });
var ent = dbcontext.Entity.FirstOrDefault(e => e.Type == type && e.Id == id);
if(ent == null)
return Json(new { status="fail", message= "No entity with that type/id was found" });
else
return Json(new { status="ok", entityName= ent.Name });
}
In one of our apps we do use HTTP errors to alter the behavior of the client - there's a promise chain at the client react app and we use returning an error to halt processing of the chain, parse the error out, then go back to parsing knowing the JSON is a slightly different shape. I'm not convinced it's a great way to do it as it's made the client more complex than it could have been but if you want to do that then sure, look at ways of returning meaningful http errors. Personally (and GPW alludes to it) even debugging, many times I've missed the return code in postman, got a blank response, been misled that something else is wrong than what is actually happening/ got a 404 and thought the back end couldn't find the entity when actually I'd got the URL wrong and was getting a 404 for a different reason etc

Edit BadRequest

I have an Response object that I want to return to the user if there is an exception in the controller. However when i try to send back BadRequest i cant seem to send back my Response object. So my question is how do i edit BadRequst to contain my Response object and/or how do i send back my Response object with an error status code ?
Controller
public async Task<IActionResult> Login([FromBody] LoginViewModel model) {
Response response = new Response();
try {
if (ModelState.IsValid) {
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
logger.LogInformation(1, "User logged in.");
return Json("User logged in.");
}
if (result.RequiresTwoFactor) {
response.id = 1 ;
throw new LoginException("Login requiest two factor", new InvalidOperationException());
}
if (result.IsLockedOut) {
response.id = 2 ;
logger.LogWarning(2, "User account locked out.");
throw new LoginException("User account locked out", new InvalidOperationException());
}
else {
response.id = 3 ;
throw new LoginException("Invalid login attempt", new InvalidOperationException());
}
}
response.id = 4 ;
var modelErrors = ModelState.Values.ToList();
throw new LoginException("Model State Error", ModelState, new InvalidOperationException());
} catch (LoginException ex){
return BadRequest(response); // response with status: 400 Bad Request for URL, no response object is in here.
}
}
Front-End
login(email:any, password:any, remember:any){
//let body:User = {email:email, password:password};
let headers = new Headers({ 'Content-Type': 'application/json' });
let body = {Email:email, Password:password,RememberMe:false };
console.log(body);
this.http.post('/api/Account/Login', body ,{headers:headers})
.map(response => response.json())
.subscribe(
response => {
console.log("Success !!!:\n"+response);
this.router.navigate(['/home']);
},
error => {
console.log("Error !!!:\n"+error);
}
);
}
It's not really clear what you're asking?
You only want to return status code 400? Use the parameterless version of return BadRequest()
Want a different status code? Use different Method. BadRequest is named by the HTTP Statuscode 400 (BadRequest). If you want to set the status code yourself, use return Status(HttpStatusCode.Unauthorized); or whatever.
Want to return a error message as json? Use error classes or annonymous class to do so:
return BadRequest(new { ErrorMessage = "Account is locked." });
But whatever you want to do, throwing exceptions in Controller is wrong. An controller action should NEVER throw an exception. Also as pointed in the comments, exceptions are to be used for exceptional cases. A wrong password, locked user or wrong authentication method (i.e. no two factor auth when its required) are expected errors and shouldn't be handled via excpetions.
Exceptions (when thrown only) are inherently expensive operations in a computer program. For that reason Microsoft implemented an IdentityResult class to return a list of expected errors, rather than throwing exceptions during the call of signInManager.PasswordSignInAsync
Instead of returning IActionResult why don't you return a HttpResponseMessage and then you can do something like:
return Request.CreateResponse(System.Net.HttpStatusCode.BadRequest)

Http.post from Angular 2 to ASP.NET ApiController

ASP.NET
[HttpPost]
[Route("apitest")]
public string apitest([FromBody]string str)
{
Console.Writeline(str); // str is always null
return null;
}
Angular 2:
var creds = "str='testst'" ;
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
http.post('http://localhost:18937/apitest', creds, {
headers: headers
})
.map(res => res.json())
.subscribe(
(res2) => {
console.log('subsribe %o', res2)
}
);
I also tried creds = {"str":"test"}; without headers JSON.stringify() etc. without success. How do I Post data to ASP.NET?
var creds = {
str: 'testst'
};
$http.post('http://localhost:18937/apitest', JSON.stringify(creds));
No changes in Web API controller and it should work.
This is probably an issue with the way that ASP.NET and MVC handle data POSTS.
[HttpPost]
public ActionResult Index(int? id)
{
Stream req = Request.InputStream;
req.Seek(0, System.IO.SeekOrigin.Begin);
string json = new StreamReader(req).ReadToEnd();
InputClass input = null;
try
{
// assuming JSON.net/Newtonsoft library from http://json.codeplex.com/
input = JsonConvert.DeserializeObject<InputClass>(json)
}
catch (Exception ex)
{
// Try and handle malformed POST body
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//do stuff
}
You can refer to my answer here and the referenced links as to a potential cause of the issue. There are quite a few server side web frameworks that inappropriately handle data POSTS and by default doesn't add the data to your request object.
You shouldn't [have to] try and change the behavior of your angular post, and modify headers to pretend your data post is a form post.

ASP.NET Web API : Correct way to return a 401/unauthorised response

I have an MVC webapi site that uses OAuth/token authentication to authenticate requests. All the relevant controllers have the right attributes, and authentication is working ok.
The problem is that not all of the request can be authorised in the scope of an attribute - some authorisation checks have to be performed in code that is called by controller methods - what is the correct way to return a 401 unauthorised response in this case?
I have tried throw new HttpException(401, "Unauthorized access");, but when I do this the response status code is 500 and I get also get a stack trace. Even in our logging DelegatingHandler we can see that the response is 500, not 401.
You should be throwing a HttpResponseException from your API method, not HttpException:
throw new HttpResponseException(HttpStatusCode.Unauthorized);
Or, if you want to supply a custom message:
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "Oops!!!" };
throw new HttpResponseException(msg);
Just return the following:
return Unauthorized();
As an alternative to the other answers, you can also use this code if you want to return an IActionResult within an ASP.NET controller.
ASP.NET
return Content(HttpStatusCode.Unauthorized, "My error message");
Update: ASP.NET Core
Above code does not work in ASP.NET Core, you can use one of these instead:
return StatusCode((int)System.Net.HttpStatusCode.Unauthorized, "My error message");
return StatusCode(Microsoft.AspNetCore.Http.StatusCodes.Status401Unauthorized, "My error message");
return StatusCode(401, "My error message");
Apparently the reason phrase is pretty optional (Can an HTTP response omit the Reason-Phrase?)
You get a 500 response code because you're throwing an exception (the HttpException) which indicates some kind of server error, this is the wrong approach.
Just set the response status code .e.g
Response.StatusCode = (int)HttpStatusCode.Unauthorized;
To add to an existing answer in ASP.NET Core >= 1.0 you can
return Unauthorized();
return Unauthorized(object value);
To pass info to the client you can do a call like this:
return Unauthorized(new { Ok = false, Code = Constants.INVALID_CREDENTIALS, ...});
On the client besides the 401 response you will have the passed data too. For example on most clients you can await response.json() to get it.
In .Net Core You can use
return new ForbidResult();
instead of
return Unauthorized();
which has the advantage to redirecting to the default unauthorized page (Account/AccessDenied) rather than giving a straight 401
to change the default location modify your startup.cs
services.AddAuthentication(options =>...)
.AddOpenIdConnect(options =>...)
.AddCookie(options =>
{
options.AccessDeniedPath = "/path/unauthorized";
})
you can use follow code in asp.net core 2.0:
public IActionResult index()
{
return new ContentResult() { Content = "My error message", StatusCode = (int)HttpStatusCode.Unauthorized };
}
You also follow this code:
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Users doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
}
throw new HttpResponseException(response);
Make sure that the lines order in "Startup.cs" is like this, not vise versa:
app.UseAuthentication(); // the order is important
app.UseAuthorization();
That was what cased the issue in my case.
Because I found this post as best match.
For a ASP.NET Core Web Api the ReturnType ContentResult is a good choice:
[HttpPost]
[Route("api/my-controller")]
public async ContentResult Index([FromBody] MyRequestType request)
{
if (!authenticate(request.User,request.Password)
{
return new ContentResult() { StatusCode = StatusCodes.Status401Unauthorized };
}
//Process request
var myReturnObject = await processRequest(request);
string errString = System.Text.Json.JsonSerializer.Serialize(myReturnObject);
return new ContentResult()
{
Content = errString,
StatusCode = StatusCodes.Status200OK,
};
}

How to set HTTP status code from ASP.NET MVC 3?

We're using OpenWeb js libraries on the frontend, and they have a need for the .NET middle tier to send them a specific HTTP header status code when certain types of errors occur. I tried to achieve that by doing this:
public ActionResult TestError(string id) // id = error code
{
Request.Headers.Add("Status Code", id);
Response.AddHeader("Status Code", id);
var error = new Error();
error.ErrorID = 123;
error.Level = 2;
error.Message = "You broke the Internet!";
return Json(error, JsonRequestBehavior.AllowGet);
}
It kind of halfway worked. See screenshot:
http status code http://zerogravpro.com/temp/pic.png
Notice I achieved the Status Code of 400 in the Response Header, but I really need the 400 in the Request Header. Instead, I get "200 OK". How can I achieve this?
My URL structure for making the call is simple: /Main/TestError/400
There is extended discussion at What is the proper way to send an HTTP 404 response from an ASP.NET MVC action?
What you want to do is set Response.StatusCode instead of adding a Header.
public ActionResult TestError(string id) // id = error code
{
Response.StatusCode = 400; // Replace .AddHeader
var error = new Error(); // Create class Error() w/ prop
error.ErrorID = 123;
error.Level = 2;
error.Message = "You broke the Internet!";
return Json(error, JsonRequestBehavior.AllowGet);
}
If all you want to return is the error code, you could do the following:
public ActionResult TestError(string id) // id = error code
{
return new HttpStatusCodeResult(id, "You broke the Internet!");
}
Reference: MSDN article on Mvc.HttpStatusCodeResult.
Otherwise, if you want to return other information use
Response.StatusCode = id
instead of
Response.AddHeader("Status Code", id);
If you can't get your json result into your view, try to add this :
Response.TrySkipIisCustomErrors = true;
Before this :
Response.StatusCode = 400;
More details on this post : https://stackoverflow.com/a/37313866/9223103

Categories