I was wondering if it was possible to return a bad request with content from an MVC Controller? The only way I have been able to do this is to throw HttpException however here I can't set any content. Tried this approach to but for some odd reason I am always getting an OK back. Is it possible to do this?
public class SomeController : Controller
{
[HttpPost]
public async Task<HttpResponseMessage> Foo()
{
var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = new StringContent("Naughty");
return response;
}
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "naughty");
Set the Http status code to bad request and use Content method to send your content along with response.
public class SomeController : Controller
{
[HttpPost]
public async Task<ActionResult> Foo()
{
Response.StatusCode = 400;
return Content("Naughty");
}
}
In addition to the #Ekk's answer, make sure to check this:
ASP.NET+Azure 400 Bad Request doesn't return JSON data
Add the following entry to your 'web.config'.
<system.webServer>
<httpErrors existingResponse="PassThrough"/>
</system.webServer>
...
Of course you can.
Take a look at my Action
// GET: Student/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
I think this is best practice
to return HttpStatusCodeResult(HttpStatusCode.BadRequest); in case user does not provided a required value
to return HttpNotFound(); in case the user provided a required value but not veiled
hope this help you
You can pass in error message to the second parameter like so:
return new HttpResponseMessage(HttpStatusCode.BadRequest, "Your message here");
The TrySkipIisCustomErrors flag can be used to turn off IIS custom error handling.
[HttpGet]
public void Foo()
{
HttpContext.Response.TrySkipIisCustomErrors = true;
HttpContext.Response.StatusCode = 400;
HttpContext.Response.Write("Naughty");
}
Answer for .Net Core: Return IActionResult as documented here. For example:
public IActionResult Get(int id = 0)
{
try
{
var obj = _myRepo.Get(id);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok(obj);
}
Related
I am using ASP.Net Core 5 to create a web API. I use controllers like this
[Route("[controller]")]
[ApiController]
public class User : ControllerBase
{
...
public async Task<ActionResult<User>> GetUserByID(int id)
{
...
}
...
}
This works fine but means I keep creating defined typed classes for the data I am returning. I am interested in returning an anonymous type sometimes rather than a specific type, is this possible?
You can use IActionResult. For example:
[HttpGet, Route("getUserById/{id}")]
public async Task<IActionResult> GetUserByID(int id)
{
var data = await Something.GetUserAsync(id);
return Ok(new
{
thisIsAnonymous = true,
user = data
});
}
One thing you "could" do is to return a "string" type all the time by serializing the data - either into a JSON sting or XML. And then interpret accordingly on the client. However, you should ideally look at using the "ProducesResponseType" feature as well as several in-built helper methods to produce different responses based on different conditions - that way you can return different types based on different scenarios. See example below:
[HttpGet]
[ProducesResponseType(typeof(User), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(User), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(User), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<User>> GetUserByID(int id)
{
try
{
User model = await _userService.Get(id);
return Ok(model);
}
catch (ApiAccessException apiException)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = apiException.ApiError, TechnicalMessage = apiException.TechnicalMessage, UserFriendlyMessage = apiException.UserFriendlyMessage };
//Serialize the exception
string errorOutput = JsonConvert.SerializeObject(detail);
return Unauthorized(errorOutput);
}
catch (ApiException apiException)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = apiException.ApiError, TechnicalMessage = apiException.TechnicalMessage, UserFriendlyMessage = apiException.UserFriendlyMessage };
string errorOutput = JsonConvert.SerializeObject(detail);
return BadRequest(errorOutput);
}
catch (Exception e)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = ApiError.InternalError, TechnicalMessage = e.Message, UserFriendlyMessage = "Internal unknown error." };
string errorOutput = JsonConvert.SerializeObject(detail);
return BadRequest(errorOutput);
}
}
I have a standard Edit action in Asp.Net MVC 5 and I want to avoid throwing the unhandled exception when a get request is made without the id like ~/food/edit, so I did this.
public ActionResult Edit(int id = 0)
{
if (id == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string result = _foodAppService.GetById(id);
FoodVm food = string.IsNullOrEmpty(result)
? null
: JsonConvert.DeserializeObject<FoodVm>(result);
if (food == null)
{
return RedirectToAction("Index");
}
return View(food);
}
My question is: Is it a good practice to handled it in this way or there are more suitable strategies ?
I'm new to this asking question thing, if a should I ask in another way, just let me know, thank you for your time.
In case zero could be valid its better to do
public ActionResult Edit(int? id)
{
if (!id.HasValue)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
Or there can be more overall way to handle exceptions in MVC. You can use override of method OnException this gives you abbility to debug all exception in controllers in one method and handle them.
Just add base class to all your controllers like this:
public class BaseController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
string redirectUrl;
var exception = filterContext.Exception;
if (exception is EntityException)
{
redirectUrl = "/Content/error.html";
}
else
{
redirectUrl = "/Info/Index";
}
//do whatever you wont
Response.Redirect(redirectUrl);
}
Also use input parameters verification as Paul Swetz saied. This method is more genera, allows to intercept all exceptions, and don`t show errors to users.
Following #Fran 's advice. I built a action filter attribute called MissingParam
public class MissingParamAttribute : ActionFilterAttribute
{
public string ParamName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters.ContainsKey(ParamName))
{
if (filterContext.ActionParameters[ParamName] == null)
{
filterContext.ActionParameters[ParamName] = 0;
}
}
base.OnActionExecuting(filterContext);
}
}
in the action I did this:
[MissingParam(ParamName="id")]
public ActionResult Edit(int id)
That way I don't need to mess with method parameter, any validating happens before. This implementation follows the Open/Close principle. I extended its functionality, but I didn't changed like the code in the question.
First and foremost its a good practice to use Try-Catch.
public ActionResult Edit(int id)
{
try
{
if (id != 0 || id!=null)
{
string result = _foodAppService.GetById(id);
FoodVm food = string.IsNullOrEmpty(result) ? null:JsonConvert.DeserializeObject<FoodVm>(result);
if (food == null)
{
return RedirectToAction("Index");
}
else
{
return View(food);
}
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
catch (exception ex)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
I want to keep my return type as a string but want the response code to be a 403.
This is what I currently have.
[HttpGet]
public string test(string echoText)
{
// Not authorized
if (echoText == "403")
{
StatusCode(HttpStatusCode.Forbidden);
return "";
}
else
return echoText;
}
UPDATE
I found the below code but is there another way without throwing an exception?
throw new HttpResponseException(HttpStatusCode.Unauthorized);
You could throw a HttpResponseException inside your action:
[HttpGet]
public string test(string echoText)
{
// Not authorized
if (echoText == "403")
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
else
return echoText;
}
I am disgusted not have found a solution to this problem.
I started creating a new api using Web API 2 and just cannot get the POST and PUT to work. The Get all and Get single item works perfectly fine.
There are no related articles anywhere, and those that i've found relates only to Gets and Web API, but not Web API 2.
Any assistance would do please.
// POST: api/checkOuts
[HttpPost]
[ResponseType(typeof(checkOut))]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IHttpActionResult> PostcheckOut(checkOut co)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.id }, checkOut);
}
So basically, I'm just attempting to get a debug into the method.
Was especially disappointed in this link as it covered almost everything, but ai. http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing
Regards
This is a working code
// POST api/values
[HttpPost]
[ResponseType(typeof(CheckOut))]
public async Task<IHttpActionResult> Post([FromBody] CheckOut checkOut)
{
if (checkOut == null)
{
return BadRequest("Invalid passed data");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.Id }, checkOut);
}
I've declared CheckOut class to be like this :
public class CheckOut
{
public int Id { get; set; }
public string Property2 { get; set; }
}
The Key things here are :
1- You need to add [FromBody] to your Api method.
2- I've tested it using Fiddler,
i- by choosing POST action.
ii- content-type: application/json.
iii- passing {"Id":1,"Property2":"Anything"} in the message body.
Hope that helps.
Given the following:
[HttpGet]
[ActionName("GetContent")]
public HttpResponseMessage GetContent(int id)
{
Content content = _uow.Contents.GetById(id);
if (content == null)
{
var message = string.Format("Content with id = {0} not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, content);
}
}
and:
[HttpGet]
[ActionName("GetContent")]
public HttpResponseMessage GetContent(int id)
{
try
{
Content content = _uow.Contents.GetById(id);
if (content == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return Request.CreateResponse<Content>(HttpStatusCode.OK, content);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
I have seen two coding styles. One using exceptions and the other not. One using CreateResponse<> and the other CreateResponse(). Can someone tell what are the advantages / disadvantages of using these? As far as I can see the second method seems to look more complete but is it really needed to use a try / catch for something as simple as this?
The main benefit to throwing HttpResponseException is when your action method returns a model type rather than an HttpResponseMessage. For example:
public Product Get(int id)
{
Product p = _GetProduct(id);
if (p == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return p;
}
This is equivalent to the following:
public HttpResponseMessage Get(int id)
{
Product p = _GetProduct(id);
if (p == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, p);
}
It's OK to choose either style.
You shouldn't catch HttpResponseExceptions, because the point is for the Web API pipeline to catch them and translate them into HTTP responses. In your second code example, the Not Found error gets caught and turned into a Bad Request error, when you really wanted the client to receive Not Found (404).
Longer answer:
CreateResponse vs CreateResponse<T> has nothing to do with using HttpResponseException.
CreateResponse returns an HTTP response with no message body:
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
CreateResponse<T> takes an object of type T and writes the object into the body of the HTTP response:
public HttpResponseMessage Get()
{
Product product = new Product();
// Serialize product in the response body
return Request.CreateResponse<Product>(HttpStatusCode.OK, product);
}
The next example is exactly the same but uses type inference to leave out the generic type parameter:
public HttpResponseMessage Get()
{
Product product = new Product();
// Serialize product in the response body
return Request.CreateResponse(HttpStatusCode.OK, product);
}
The CreateErrorResponse method creates an HTTP response whose response body is an HttpError object. The idea here is to use a common message format for error responses. Calling CreateErrorResponse is basically the same as this:
HttpError err = new HttpError( ... )
// Serialize err in the response.
return Request.CreateResponse(HttpStatusCode.BadRequest, err);