I'm using the Asp.Net WebClient to create an HTTP post.
The below code has try-catch block around the code which catches WebException:
try
{
using (MyWebClient wc = new MyWebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = _lender.ContentType;
wc.Timeout = 200;
return _lender.GetResult(wc.UploadString(_lender.PostUri, _lender.PostValues));
}
}
catch (WebException ex)
{
return new ServiceError(ex.Status.ToString());
}
The main exceptions I'm looking for are timeouts. I've extended WebClient to allow me to set the timeout.
When I set the timeout to say 100ms, an exception is thrown as expected. I can get the WebException status as per the example (it returns "timeout"), however, I want to return status codes too.
If I extract the httpwebresponse using ex.Response I get a null value returned, when I was expecting an associated status code.
Why do I not get an HttpStatus.Request.Timeout?
I have the same problem and I realise a few things while I search for a solution.
WebExceptionStatus enum is not equivalent to http status code that the API you call returned. Instead it is a enum of possible error that may occour during a http call.
The WebExceptionStatus error code that will be returned when you receive an error (400 to 599) from your API is WebExceptionStatus.ProtocolError aka number 7 as int.
When you need to get the response body or the real http status code returned from the api, first you need to check if WebException.Status is WebExceptionStatus.ProtocolError. Then you can get the real response from WebExceptionStatus.Response and read its content.
Sometimes the timeout is handled by the caller (aka your code) so you do not have a response in that case. So you can look if WebException.Status is WebExceptionStatus.Timeout
This is an example:
try
{
...
}
catch (WebException webException)
{
if (webException.Status == WebExceptionStatus.ProtocolError)
{
var httpResponse = (HttpWebResponse)webException.Response;
var responseText = "";
using (var content = new StreamReader(httpResponse.GetResponseStream()))
{
responseText = content.ReadToEnd(); // Get response body as text
}
int statusCode = (int)httpResponse.StatusCode; // Get the status code
}
else if (webException.Status == WebExceptionStatus.ProtocolError)
{
// Timeout handled by your code. You do not have a response here.
}
// Handle other webException.Status errors. You do not have a response here.
}
Related
I am using as Web API what uses AuthorisationManager owin middleware to handle token based security.
My problem is that various errors within the response body have various different formats.
Within my api, I usually send errors back with the structure
{"code": "error code", "message": "error message"}
However some of the errors coming from the security may use
{"error": "error code", "error_description": "error message"}
or sometimes just
{"error": "error mesage"}
I would like to unify these to all have the same structure I use elsewhere, ie the
{"code": "error code", "message": "error message"}
I have seen quite a few posts on replacing a response body.
I first tried this method, ie using the DelegatingHandler. This worked in most cases, but it did not catch my authorization failed error messages comding out of my OAuthAuthorizationServerProvider
I next tried using a middleware approach as shown here.
Here is my full interpretation..
public override async Task Invoke(IOwinContext context)
{
try
{
// hold a reference to what will be the outbound/processed response stream object
var stream = context.Response.Body;
// create a stream that will be sent to the response stream before processing
using (var buffer = new MemoryStream())
{
// set the response stream to the buffer to hold the unaltered response
context.Response.Body = buffer;
// allow other middleware to respond
await this.Next.Invoke(context);
// Error codes start at 400. If we have no errors, no more to d0.
if (context.Response.StatusCode < 400) // <---- *** COMMENT1 ***
return;
// we have the unaltered response, go to start
buffer.Seek(0, SeekOrigin.Begin);
// read the stream
var reader = new StreamReader(buffer);
string responseBody = reader.ReadToEnd();
// If no response body, nothing to do
if (string.IsNullOrEmpty(responseBody))
return;
// If we have the correct error fields names, no more to do
JObject responseBodyJson = JObject.Parse(responseBody);
if (responseBodyJson.ContainsKey("code") && responseBodyJson.ContainsKey("message"))
return;
// Now we will look for the known error formats that we want to replace...
byte[] byteArray = null;
// The first one from the security module, errors come back as {error, error_description}.
// The contents are what we set (so are correct), we just want the fields names to be the standard {code, message}
var securityErrorDescription = responseBodyJson.GetValue("error_description");
var securityErrorCode = responseBodyJson.GetValue("error");
if (securityErrorDescription != null && securityErrorCode != null)
byteArray = CreateErrorObject(securityErrorCode.ToString(), securityErrorDescription.ToString());
// The next horrible format, is when a refresh token is just sends back an object with 'error'.
var refreshTokenError = responseBodyJson.GetValue("error");
if (refreshTokenError != null)
{
// We will give this our own error code
var error = m_resourceProvider.GetRefreshTokenAuthorisationError(refreshTokenError.ToString());
byteArray = CreateErrorObject(error.Item2, error.Item3);
}
else
{
byteArray = Encoding.ASCII.GetBytes(responseBody);
}
// Now replace the response (body) with our now contents
// <---- *** COMMENT2 ***
context.Response.ContentType = "application / json";
context.Response.ContentLength = byteArray.Length;
buffer.SetLength(0);
buffer.Write(byteArray, 0, byteArray.Length);
buffer.Seek(0, SeekOrigin.Begin);
buffer.CopyTo(stream);
}
}
catch (Exception ex)
{
m_logger.WriteError($"ResponseFormattingMiddleware {ex}");
context.Response.StatusCode = 500;
throw;
}
}
private byte[] CreateErrorObject(string code, string message)
{
JObject newMessage = new JObject();
newMessage["code"] = code;
newMessage["message"] = message;
return Encoding.ASCII.GetBytes(newMessage.ToString());
}
So this basically seemed to work, and catch ALL responses, which is good.
However, what I was hoping to do, is, when there is no error, (or the error is already in the correct format), just pass the response on without doing anything with it.
I am mainly thinking of some of my GETs, where the data may be large, I was hoping to avoid having to do the extra copying back. In the above code, where I have marked *** COMMENT1 ***, I have an early return to try to avoid this, ie the line...
// Error codes start at 400. If we have no errors, no more to d0.
if (context.Response.StatusCode < 400)
return;
The problem, is when I do this, I get no body at all returned, ie no data for all the GET calls, etc.
Is there a way to avoid this extra copying (ie at the line *** COMMENT2 ***) when we don't want to do any modifications?
Thanks in advance for any suggestions.
Adding an answer since it has a code snippet but this is really just a comment.
Our services use the delegatingHandler approach you mentioned you tried first. Do you have try/catch around the call to base.SendAsync. In this snippet the requestState is just a wrapper around the incoming request with some timers, loggers, etc. In many cases we replace the response as you are trying. I stepped through the exception and used the VS debugger immediate window to modify the error response. It works for me.(TM)
try
{
return base
.SendAsync(request, cancellationToken)
.ContinueWith(
(task, requestState) => ((InstrumentedRequest)requestState).End(task),
instrumentedRequest,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default)
.Unwrap();
}
catch (Exception ex)
{
instrumentedRequest.PrematureFault(ex);
throw;
}
I have implemented a #OneWay JAX-RS service with Apache CXF ( a dropwizard application ). When called with invalid structure, causing an unmarshalling error in DocLiteralInInterceptor, http status code 200 is returned to client. To make the calling process recognize the fault, I need to return status 400 or 500, along with the error text from Unmarshalling Error.
I recognized that, after the error, the "in" interceptor chain is unwound ( interceptors handleFault-methods are called in reverse order ), so I installed an interceptor at the start of the "in"-chain ( last on unwinding ) with
public CustomSOAPInterceptor(String chainname) {
super(Phase.RECEIVE);
getBefore().add(PolicyInInterceptor.class.getName());
this.chainname=chainname;
}
Within my handleFault-Method I can seperate the fault message and recognize the unmarshall error. But I am not succeeding in setting the response.
I tried
Fault f = (Fault) e;
f.setStatusCode(Response.Status.BAD_REQUEST.getStatusCode());
and
Response response = Response
.status(Response.Status.BAD_REQUEST)
.entity(Response.Status.BAD_REQUEST.getStatusCode() + " " + f.getLocalizedMessage())
.build();
soapMessage.getExchange().put(Response.class, response);
and
message.put(Message.RESPONSE_CODE, Response.Status.BAD_REQUEST.getStatusCode());
Where is the response set and how can I overwrite it ?
Tx for any advice.
I know, its kind of late, but for those who are looking for a solution:
In my application, the following works:
public void handleFault(SoapMessage soapMessage) {
/* some code to test for specific error deleted */
Exchange exchange = soapMessage.getExchange();
Message outMessage = exchange.getOutMessage();
if (outMessage == null) {
Endpoint endpoint = exchange.get(Endpoint.class);
outMessage = endpoint.getBinding().createMessage();
exchange.setOutMessage(outMessage);
}
try {
EndpointReferenceType target = exchange.get(EndpointReferenceType.class);
Conduit conduit = exchange.getDestination().getBackChannel(soapMessage);
exchange.setConduit(conduit);
conduit.prepare(outMessage);
} catch (IOException ex) {
LOG.error(ex.getMessage(), ex);
}
Object resp = outMessage.get("HTTP.RESPONSE");
if (resp != null && resp instanceof HttpServletResponse) {
HttpServletResponse response = (HttpServletResponse) resp;
response.setStatus(Response.Status.BAD_REQUEST.getStatusCode());
}
soapMessage.getInterceptorChain().abort();
}
I have a function in my #Service class that is marked with #HystrixCommand.
This method acts as a client which sends a request to another service URL and gets back a response.
What I want to do is to trigger a fallback function when the response status code is anything other than 200. It will also trigger a fallback for any other exceptions (RuntimeExceptions etc.).
I want to do this by making use of the #HystrixProperty or #HystrixCommandProperty.
I want the client to ping the URL and listen for a 200 response status and if it does not get back a 200 status within a certain time-frame I want it to fallback.
If it gets back a 200 status normally within a certain time it should not trigger the fallback.
#HystrixCommand(fallbackMethod="fallbackPerformOperation")
public Future<Object> performOperation(String requestString) throws InterruptedException
return new AsyncResult<Object>() {
#Override
public Object invoke() {
Client client = null;
WebResource webResource = null;
ClientResponse response =null;
String results = null;
try{
client = Client.create();
webResource = client.resource(URL);
client.setConnectTimeout(10000);
client.setReadTimeout(10000);
response = webResource.type("application/xml")
.post(ClientResponse.class, requestString);
} finally {
client.destroy();
webResource = null;
}
return results;
}
};
}
I specifically want to make use of the #HystrixProperty or #HystrixCommandProperty so performing a check inside the method for response status code not being 200 and then throwing an Exception is not acceptable.
Instead of using Annotations will creating my own Command by extending the HystrixCommand Interface work?
Any ideas or resources for where I can start with this are more than welcome.
I don’t understand why you don’t want to check the response http status code and throw an exception if it is not 200? Doing that will give you the behaviour you desire. i.e. it will trigger a fall back for exceptions or non 200 responses.
You can set the timeout in the client, however I would opt for using the hystrix timeout values. That way you can use Archaius to dynamically change the value at runtime if desired.
You can use the Hystrix command annotation or extend the HystrixCommand class. Both options will provide you with your desired behaviour
Here is an example using the annotation.
#HystrixCommand(fallbackMethod = "getRequestFallback")
public String performGetRequest(String uri) {
Client client = Client.create();
WebResource webResource = client.resource(uri);
ClientResponse response = webResource.get(ClientResponse.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Invalid response status");
}
return response.getEntity(String.class);
}
public String getRequestFallback(String uri) {
return "Fallback Value";
}
If there's an unhandled server error 500 in ASP.NET MVC, the server returns a HTML page like this:
Question: is it possible to configure the application so that it returns a JSON with the same information instead of the above HTML?
eg:
{
Title:'Maximum request length exceeded',
Description:'An unhandled eception .....',
...etc
}
you need to catch the error somehwere appropriate [i suggest use custom error filter on the controller for example that inherits from HandleErrorAttribute], and override OnException method, from there you can check if it is Ajax, then do something else, here is a snippet that i wrote before (not clean yet)
and dont forget to set the status code!
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
JsonResultObject result = new JsonResultObject();
result.Success = false;
string message = ("Common.WeAreFixing" + " , #" + errorLog.Id.ToString("00000"));
if (filterContext.HttpContext.Request.IsLocal)
{
message = filterContext.Exception.Message + Environment.NewLine + "st: " +
(filterContext.Exception.StackTrace ?? "");
}
result.AlertMessage = new Alert(message, Alert.Type.Error);
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonDotNetResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = result
};
filterContext.HttpContext.Items["ErrorNumber"] = errorLog.Id.ToString("00000");
}
Within exceptions there is sensitive information including stack details that should not be leaked to the consumer however as you are showing the error screen i am presuming that this is running in a debug environment and that is not an issue.
Also in a non debug environment the exception details may be stripped out of the response so ideally you should form a custom message of Json format that is based off that exception and then log the original stack details so you can handle the issue at a later date.
Something like the below should get you started:
try
{
// do some work
}
catch (ExplicitException ex)
{
// Log the exception(ex);
var message = "Error Doing Work");
return Json(new { status = "Error", message });
}
}
I need to check whether the request will return a 500 Server Internal Error or not (so getting the error is expected). I'm doing this:
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.OK)
return true;
else
return false;
But when I get the 500 Internal Server Error, a WebException is thrown, and I don't want to depend on it to control the application flow - how can this be done?
I think this MSDN articles will help you:
http://msdn.microsoft.com/en-us/library/system.net.webexception.status.aspx
Indeed, given the example at msdn, there is no way to not depend on the exception for control flow. Here's the example they give:
try {
// Create a web request for an invalid site. Substitute the "invalid site" strong in the Create call with a invalid name.
HttpWebRequest myHttpWebRequest = (HttpWebRequest) WebRequest.Create("invalid site");
// Get the associated response for the above request.
HttpWebResponse myHttpWebResponse = (HttpWebResponse) myHttpWebRequest.GetResponse();
myHttpWebResponse.Close();
}
catch(WebException e) {
Console.WriteLine("This program is expected to throw WebException on successful run."+
"\n\nException Message :" + e.Message);
if(e.Status == WebExceptionStatus.ProtocolError) {
Console.WriteLine("Status Code : {0}", ((HttpWebResponse)e.Response).StatusCode);
Console.WriteLine("Status Description : {0}", ((HttpWebResponse)e.Response).StatusDescription);
}
}
catch(Exception e) {
Console.WriteLine(e.Message);
}
Apparently, sometimes you do have to go down that route. Ah, well.