Is there a way of changing Web Api's default behavior for error messages, such as:
GET /trips/abc
Responds with (paraphrased):
HTTP 500 Bad Request
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'tripId' of non-nullable type 'System.Guid' for method 'System.Net.Http.HttpResponseMessage GetTrip(System.Guid)' in 'Controllers.TripController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
I'd like to avoid giving out this rather detailled information about my code, and instead replace it with something like:
HTTP 500 Bad Request
{
error: true,
error_message: "invalid parameter"
}
I'd be able to do this inside the UserController, but the code execution doesn't even go that far.
edit:
I've found a way of removing detailed error messages from the output, using this line of code in Global.asax.cs:
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.LocalOnly;
This produces a message like this:
{
"Message": "The request is invalid."
}
which is better, however not exactly what I want - We've specified a number of numeric error codes, which are mapped to detailed error messages client-side. I would like to only output the appropriate error code (that I'm able to select prior to output, preferrably by seeing what kind of exception occured), for example:
{ error: true, error_code: 51 }
You might want to keep the shape of the data as the type HttpError even if you want to hide detailed information about the actual exception. To do that, you can add a custom DelegatingHandler to modify the HttpError that your service throws.
Here is a sample of how the DelegatingHandler might look like:
public class CustomModifyingErrorMessageDelegatingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
{
HttpResponseMessage response = responseToCompleteTask.Result;
HttpError error = null;
if (response.TryGetContentValue<HttpError>(out error))
{
error.Message = "Your Customized Error Message";
// etc...
}
return response;
});
}
}
Maggie's answer worked for me as well. Thanks for posting!
Just wanted to some bits to her code for additional clarification:
HttpResponseMessage response = responseToCompleteTask.Result;
HttpError error = null;
if ((!response.IsSuccessStatusCode) && (response.TryGetContentValue(out error)))
{
// Build new custom from underlying HttpError object.
var errorResp = new MyErrorResponse();
// Replace outgoing response's content with our custom response
// while keeping the requested MediaType [formatter].
var content = (ObjectContent)response.Content;
response.Content = new ObjectContent(typeof (MyErrorResponse), errorResp, content.Formatter);
}
return response;
Where:
public class MyErrorResponse
{
public MyErrorResponse()
{
Error = true;
Code = 0;
}
public bool Error { get; set; }
public int Code { get; set; }
}
Related
I have axios request in my vue application:
async get_banner(id:number) : Promise<any> {
return global.axios.get(`${process.env.VUE_APP_DOMAIN}/banners/${id}`)
}
it works while banner/${id} response exits, but I have situation when I should disable banner in my admin panel so api endpoint becomes empty. (not exits) so I am getting Request failed with status code 404 in my vue app console.
question is how to prevent error and know if url exits or not? what is best practice to do this?
You can't tell whether an API exists or not without trying it (or relying on another API to get status of the former API)
It's usually just a manner of handling the response properly. Usually this would look something like this...
getTheBanner(id){
this.errorMessage = null; // reset message on request init
get_banner(id)
.then(r => {
// handle success
this.results = r;
})
.catch(e => {
// handle error
if (e.status === 404){
// set error message
this.errorMessage = "Invalid banner Id";
}
})
}
then in your template you could have something like this
<div v-if="errorMessage" class="alert danger">{errorMessage}</div>
Explaination:
Yes, you're absolutely right. This is the default behavior of strapi. Whenever the response is empty it throws a 404 error. This is basically because the findOne method in service returns null to the controller and when the controller sends this to the boom module it returns a 404 Not Found error to the front end.
Solution:
Just override the find one method in the controller to return an empty object {} when the response is null.
Implementation
// Path - yourproject/api/banner/controllers/banner.js
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
/**
* Retrieve a record.
*
* #return {Object}
*/
async findOne(ctx) {
const { id } = ctx.params;
const entity = await strapi.services.restaurant.findOne({ id });
// in case no entity is found, just return emtpy object here.
if(!entity) return {};
return sanitizeEntity(entity, { model: strapi.models.restaurant });
},
};
Side Note:
There's no need to make any changes to the browser side axios implementation. You should always handle such cases in controller rather the client side.
Reference:
Backend Customizations
I am implementing my own messaging endpoint for a MS Teams bot from scratch. I'm almost there. The endpoint does get called with conversationUpdate events, but I see:
There was an error sending this message to your bot: HTTP status code
BadRequest
in the admin on https://dev.botframework.com/bots/channels?id=...
I am probably returning something bad in the HTTP request. As I didn't find anything about the response in the REST API docs, I am just sending the string "{}" with a standard content type.
So what do I actually need to return?
Edit: It appears that the relevant part of the botbuilder-java package is this function in ControllerBase.java:
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
Activity activity = getActivity(request);
String authHeader = request.getHeader("Authorization");
adapter.processIncomingActivity(
authHeader, activity, turnContext -> bot.onTurn(turnContext)
).handle((result, exception) -> {
if (exception == null) {
response.setStatus(HttpServletResponse.SC_ACCEPTED);
return null;
}
if (exception.getCause() instanceof AuthenticationException) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return null;
});
} catch (Exception ex) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
As far as I can tell, this only sets a return code (202) but does not return any content. I now try to do the same thing in my messaging endpoint, but Teams still complains about "BadRequest".
Edit: I have snooped what the actual BotFramework Java sample does - it just returns a code 202 with an empty request body and no content type. I'm now doing the exact same thing, and Teams still complains that it could not send the message. Kinda giving up here.
I have broke it down to a minimum and still don't know why this happens.
I have the following method in my controller:
#RequestMapping(method = RequestMethod.POST, value = "/myGreatCall")
public String getDynamicData(#RequestBody DataRequest dr) {
return dr.toString();
}
Using the following simple class:
public class DataRequest {
private String type;
//Getters and setters here
}
Now if I try to call this, I get an error 400 as the response.
let url = window.location.protocol+"//"+window.location.host+"/myGreatCall";
let request = new XMLHttpRequest();
request.open("POST", url, true);
request.onload = function () {
console.log(request.response); //Here I read the reponse and get the error 404
};
// This is the data I send as the body
let data = JSON.stringify(
{
type: "myType"
}
);
request.setRequestHeader("Content-Type", "application/json");
request.send(data);
Now from the error I suspect that for some reason it cant map my json object into the java object, but I have no idea why.
I tested the following:
do the request without the Method Parameter, that worked
different data types in the java class
handing over a hardcoded string '{\"type\":\"myType\"}' to the #send()
Any Ideas what I might be doing wrong?
It may be down to JSON serialization. Try this:
let data = JSON.stringify(
{
"type": "myType"
}
);
Ok seems to be something weird. I dont know what caused it, but after a PC restart it worked fine.
I'm wondering what I have to return after that I call my REST API using the DELETE method. I wasn't able to find out any standard/best practice for this. At the moment my code base use 2 different approach, first of all return the deleted resource so into the Response Body I return just null. The second approach (which I don't really like) I instance a new Object and I return it. What you do think is the best way? If none of this two seems good to you, which one would be the best (practice) approach?
Here a sample of what I actually have: code sample
NB: Of course both of the described approach, are performed after the actual deleting on the DB.
After successful deletion you should return empty body and 204 No Content status code.
When returning 200 OK with empty body some clients (e.g. EmberJS) fail because they expect some content to be parsed.
I would return a HTTP 204 OK in order to signal that the request has succeeded.
If you need to return a response body, in case the deletion has triggered something, I would use a HTTP 200 OK with a body attached.
what about Returning void wich means HTTP 200 OK in case of success
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public void delete(#PathVariable("id") Long id) {
service.delete(id);
}
EDIT
In the front Controller you can use something like this:
#RequestMapping(...)
public ModelAndView deleteMySlide(Model model,...){
try {
//invoke your webservice Here if success
return new ModelAndView("redirect:/anotherPage?success=true");
} catch (HttpClientErrorException e) {
//if failure
return new ModelAndView("redirect:/anotherPage?success=false");
}
}
or :
#RequestMapping(...)
public String deleteMySlide(Model model,...){
try {
//invoke your webservice Here if success
model.addAttribute("message","sample success");
return "redirect:/successPage");;
} catch (HttpClientErrorException e) {
//if failure
model.addAttribute("message","sample failure");
return "redirect:/failurePage");
}
}
Let's say I have an RPC call for adding colors. A user can only add colors once. If they add one a second time, I want to return an error response that tells them they screwed up, and why.
The JSON-RPC error response describes an error object that includes a space for a data parameter. It is in here that it seems appropriate to include my error code for "color already added". However, I cannot figure out how to return this in the response.
$jsonRpc = new Server();
$jsonRpc->setClass(new Testi());
$jsonRpc->getRequest()->setVersion(Server::VERSION_2);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
echo $jsonRpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
}
else {
$jsonRpc->handle();
}
class Testi {
const ERROR_CODE_COLOR_EXISTS = 5;
private $colors = ['red', 'green'];
/**
* #param $color
* #return array
*/
public function addColor($color) {
if (in_array($color, $this->colors)) {
throw new \Exception('Color exists');
}
else {
$this->colors[] = $color;
}
return $this->colors;
}
}
This works, to the degree that an error response is returned, but gives me no option to include my error code (self::ERROR_CODE_COLOR_EXISTS).
{"error":{"code":-32000,"message":"Color exists","data":{}},"id":"","jsonrpc":"2.0"}
How do I put info into that DATA parameter!?
Thanks,
Adam
Turns out you have two choices to do this:
1) Add parameters to an exception:
$e = new Exception('I pooped my pants');
$e->color = 'brown';
$->smell = 'bad';
color and smell would then be in the data parameter of the error response.
2) Pass the server (in my code, $jsonRpc) into the object (in my code, it would look something like: new Testi($jsonRpc)), and use the fault(...) method, which allows you to pass the data array/object into it.
The latter approach gives you more flexibility as you can do data->code, data->message, neither of which can you set on the $e object, because they are existing, protected parameters. But you are then coupling your model to the $jsonRpc server, which is not good.
All that being said, it's not the correct way to respond to the scenario I outlined above. The error response is more or less reserved for true, unrecoverable server errors, akin to real exceptions, not user validation errors. In my case it was better to define a response type that allows me to return success/fail values with appropriate response codes. {success: false, code: 5}.
Cheers,
Adam