I have a OWIN middleware class to do some authentication based on some custom tokens. All works fine. However I would like to return a useful error response to the client. My reasoning is that if the client asked for a 'application/json' response and they are expecting a serialize object, then that's what they should get, even if it is a 401 status code.
Here is the Invoke section of my middleware:
public override async Task Invoke(IOwinContext context)
{
try
{
this.DoAuthorization(context);
await this.Next.Invoke(context);
}
catch (UnauthorizedAccessException ex)
{
this.GenerateErrorResult(context, HttpStatusCode.Unauthorized, this.ExceptionToString(ex));
}
catch (Exception ex)
{
this.GenerateErrorResult(context, HttpStatusCode.InternalServerError, this.ExceptionToString(ex));
}
}
private void GenerateErrorResult(IOwinContext context, HttpStatusCode code, string errorMessage)
{
var result = new Result { Status = Result.EStatus.Error, ErrorText = errorMessage };
context.Response.StatusCode = (int)code;
context.Response.ContentType = "application/json";
context.Response.Write(JsonConvert.SerializeObject(result));
}
This all works fine, however:
is this the 'correct' way?
what if the client asks for 'application/xml', which obviously Web API is quite capable of supporting
Is there a better way to return a custom response object ('Result' in my case) that is serialized as the client would expect?
Well this seems to work, using an extra OwinMiddleware inserted first:
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (UnauthorizedAccessException ex)
{
var result = new Result { Status = Result.EStatus.Error, ErrorText = ExceptionToString(ex) };
this.ReturnFormattedResult(result, HttpStatusCode.Unauthorized, context);
}
catch (Exception ex)
{
var result = new Result { Status = Result.EStatus.Error, ErrorText = ExceptionToString(ex) };
this.ReturnFormattedResult(result, HttpStatusCode.InternalServerError, context);
}
}
private void ReturnFormattedResult(Result result, HttpStatusCode code, IOwinContext context)
{
// what should our response be?
var mediaType = context.Request.MediaType ?? context.Request.ContentType;
// use the accept header (unless it is empty or '*/*' in which case use the content-type
if (!string.IsNullOrEmpty(context.Request.Accept) && !context.Request.Accept.Contains("*/*"))
{
mediaType = context.Request.Accept;
}
// find a formatter for this media type, if no match then use the first one
var formatter = this.config.Formatters.FindWriter(typeof(Result), new MediaTypeHeaderValue(mediaType));
if (formatter == null)
{
formatter = this.config.Formatters.First();
mediaType = formatter.SupportedMediaTypes.First().MediaType;
}
context.Response.StatusCode = (int)code;
context.Response.ContentType = mediaType;
formatter.WriteToStreamAsync(typeof(Result), result, context.Response.Body, null, null).Wait();
}
Related
I'm working on Xamarin.Forms App connected with Web Api 2 Api and all requests and responses work with HttClient. This is my code:
class for all my requests and definiot of HttpClient
public class DataStore : IDataStore<object>
{
HttpClient client;
public DataStore()
{
client = new HttpClient()
{
BaseAddress = new Uri($"{App.Uri}")
};
}
Example of one of my requests :
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/User/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
My idea is to check every response(Response Status Code) in one place. I need this for throw Alert Errors , for refresh token etc. Is there a possible way to this ? I want to have control on every request/response.
if anyone have problem with this , just need to implement custom handler , who will inherit form DelegatingHandler. My code example:
public class StatusCodeHandler : DelegatingHandler
{
public StatusCodeHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
public GetStatusCode GetStatusCode = new GetStatusCode();
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
response = await base.SendAsync(request, cancellationToken);
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
var status_code = (int)response.StatusCode;
GetStatusCode.GetResponseCode(status_code);
}
return response;
}
}
This is not related to xamarin, its a question of abstraction in OOP. You can and should abstract HttpClient and its methods to remove all the boilerplate.
Example - GetAsync<T>(url) will check for connectivity, forms request adds necessary headers, waits for response, checks response status, reads response and finally returns the deserialised response. That way, if you want to add caching layer it's easier. Basic OOP.
Abstracting your code:
public async Task<T> GetAsync(string url)
{
try
{
if (!CrossConnectivity.Current.IsConnected)
{
// throw custom exception?
new NoNetworkException();
}
var token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
// read response and throw for logging?
new InvaidResponseException();// custom exceptions makes it easier for catching
}
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
// there should be an async overload to read too
var jsonMessage = new StreamReader(responseStream).ReadToEnd();
return JsonConvert.DeserializeObject<T>(jsonMessage);
}
}
catch(NoNetworkException ex)
{
// handle
}
catch(InvaidResponseException ex)
{
// handle
}
}
I am trying to test my API by calling an endpoint which should return an error message (json response). When testing this in Postman, the API indeed returns correct JSON and I am able to see that the status code is not 200. However, when trying to test this while using xunit and HttpClient, I am getting the following error message:
System.Net.Http.HttpRequestException : Error while copying content to a stream.
I am trying to figure out why this is happening. In my API I am checking credentials and if they are not correct, I will throw an Exception. This exception will get caught by a global exception handler, which will set the correct status code and create a json response.
Exception handler:
public class ExceptionHandler : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
int status = (int)HttpStatusCode.InternalServerError;
String message = context.Exception.Message;
var exception = context.Exception;
if (exception is ArgumentNullException)
{
message = ((ArgumentNullException)exception).ParamName + " is required";
status = (int)HttpStatusCode.BadRequest;
}
if (exception is NotFoundException)
{
message = exception.Message;
status = (int)HttpStatusCode.NotFound;
}
if (exception is AuthenticationException)
{
message = exception.Message;
status = (int)HttpStatusCode.Unauthorized;
}
if (exception is DuplicateEntryException)
{
message = exception.Message;
status = 422; // Unprocessable entity, not supported in HttpStatusCode class
}
HttpResponse response = context.HttpContext.Response;
response.StatusCode = status;
response.ContentType = "application/json";
response.WriteAsync(JsonConvert.SerializeObject(new ErrorMessage(message, (int)status)));
}
}
class ErrorMessage
{
public string Message { get; set; }
public int Code { get; set; }
public ErrorMessage(string message, int code)
{
Message = message;
Code = code;
}
}
Integration test:
[Fact]
public async Task ItReturnsAnErrorWhenCredentialsAreIncorrect()
{
var request = new UserAuthenticationRequest();
request.Username = "JohnDoe";
request.Password = "TestPasswordIncorrect";
var stringPayload = JsonConvert.SerializeObject(request);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await _client.PostAsync("api/authentication/GetUserAppToken", httpContent);
Assert.Equal(401, (int)response.StatusCode);
}
Anyone having any idea why it is throwing the HttpRequestException?
Update:
Client setup:
public AuthenticationControllerTest()
{
_testServer = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
_client = _testServer.CreateClient();
}
I did a test by setting up a new client per test method. Unfortunately that also didn't solve the problem
Update 2:
I think it has to do something with either the HttpClient setup or the request message I am sending. Instead of making use of the exception handler, I decided to return a BadRequest() from the controller right away.
public BadRequestResult Post([FromBody]UserAuthenticationRequest userAuthenticationRequest)
{
return BadRequest();
}
When doing that, I am getting a HttpRequestException again, instead of getting a HttpResponse with status code 400.
I'm working on an async http call using HttpClient. The call is made inside an async task. The call is successful and I get a response from the Http call. But when I try to return the response from the task nothing happens, even though I have a breakpoint waiting after the return.
public void ExecuteTask(Foundation.Security.SecurityToken token, Order order)
{
ExecuteTaskAsync(token, order).Wait();
}
public async Task ExecuteTaskAsync(Foundation.Security.SecurityToken token, Order order)
{
if (order != null)
{
log.Info("Starting export of order " + order.ID.ToString());
bool success = await ExportOrder(order, token);
if (!success)
{
log.Error("Failed to export order with ID " + order.ID.ToString());
}
}
}
private async Task<bool> ExportOrder(Order order, Foundation.Security.SecurityToken token)
{
try
{
ResponseObject response = await webService.SendOrder(new SenderInformation(token), new ReceiverInformation(order, token));
if (response.Success && response.Status.Equals("201", StringComparison.OrdinalIgnoreCase))
{
log.Info(String.Format("Order ({0}) was successfully exported"), order.ExternalOrderID);
return true;
}
return false;
}
catch (Exception e)
{
log.Error(String.Format("Exception occured while exporting order ({0})", order.ID), e);
return false;
}
}
Below is the code which does the actual http call. I marked the last functional line with the comment "The code successfully reach this line. After this nothing happens"
public Task<ResponseObject> SendOrder(SenderInformation sender, ReceiverInformation receiver)
{
OrderRequest request = new OrderRequest(sender, receiver);
return ExecuteRequest<OrderRequest, ResponseObject>(request);
}
private async Task<ResponseType> ExecuteRequest<RequestType, ResponseType> (RequestType request)
where RequestType : RequestObject
where ResponseType : class, ResponseObject, new()
{
try
{
using (var client = new HttpClient())
{
string xml = SerializeRequest(request);
HttpContent content = new StringContent(xml);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/xml");
string requestUrl = "URL";
HttpResponseMessage response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
// Parse response
if (response.IsSuccessStatusCode)
{
Stream responseStream = await response.Content.ReadAsStreamAsync();
ResponseType responseObject = DeserializeResponse<ResponseType>(responseStream);
if (responseObject != null)
{
responseObject.Success = true;
return responseObject; //The code successfully reach this line. After this nothing happens
}
else
{
log.Error("Response could not be deserialized");
}
}
else
{
log.Error("Error during request, got status code " + response.StatusCode);
}
}
}
catch (Exception e)
{
log.Error("Something went wrong!", e);
}
return new ResponseType() { Success = false };
}
The problem is on this line:
ExecuteTaskAsync(token, order).Wait();
This causes a deadlock: the awaits in the called method can't resume because the UI thread is blocked.
When you use async code, you must use it all the way; never wait synchronously for an async task to complete.
I'm trying to get my self-hosted service using Nancy to return json formatted errors upon an uncaught exception. However, I am always getting the response:
{"readyState":4,"status":404,"statusText":"error"}
(below is the amalgamation of several examples across the net).
My bootstrapper contains the following:
pipelines.OnError.AddItemToEndOfPipeline((ctx, exc) =>
{
if (exc is Exception)
{
// this is always executed upon failure to handle an exception.
Log.Error("Unhandled error on request: " + context.Request.Url + " : " + exc.Message, exc);
JsonResponse response = new JsonResponse(string.Format("{0}:{1}", exc, exc.Message), new DefaultJsonSerializer());
response.StatusCode = HttpStatusCode.InternalServerError;
return response;
}
return HttpStatusCode.InternalServerError;
});
I have a StatusCodeHandler:
public class JsonErrorStatusCodeHandler : IStatusCodeHandler
{
public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context)
{
return statusCode == HttpStatusCode.InternalServerError;
}
public void Handle(HttpStatusCode statusCode, NancyContext context)
{
var exception = context.GetException();
if (exception != null)
{
// never executed
}
// this is executed
JsonResponse response = new JsonResponse("wtf"), new DefaultJsonSerializer());
response.StatusCode = HttpStatusCode.InternalServerError;
context.Response = response;
}
Although I've verified that the code in OnError and Handle is executed (see comments), my clients still receive 404. I've also tried using
var exception = context.Items[NancyEngine.ERROR_EXCEPTION] as Exception;
instead of
var exception = context.GetException();
with no luck.
Gah, so this was a CORS issue.
I'm automatically adding the CORS headers to the response:
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
});
pipelines.OnError.AddItemToEndOfPipeline((ctx, exc) =>
{
if (exc != null)
{
throw exc;
}
return HttpStatusCode.InternalServerError;
});
base.RequestStartup(container, pipelines, context);
}
But when the response is replaced in my status code handler I need to set these headers again:
public class JsonErrorStatusCodeHandler : IStatusCodeHandler
{
public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context)
{
if (statusCode != HttpStatusCode.InternalServerError)
{
return false;
}
var exception = context.GetException();
return exception != null;
}
public void Handle(HttpStatusCode statusCode, NancyContext context)
{
var exception = context.GetException();
JsonResponse response = new JsonResponse(string.Format("{0}:{1}", exception, exception.Message), new DefaultJsonSerializer());
response.StatusCode = HttpStatusCode.InternalServerError;
context.Response = response;
context.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
}
}
I am building a .NET 4.5 MVC 4 Web API that will have publicly exposed Controller Methods that I wish to secure access to. I have created a Action Filter Attribute to check for a properly encoded RSA token shown below gutted for brevity:
public class TokenValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
try
{
//authorize user
}
catch (Exception)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("Unauthorized User")
};
}
}
}
And then in my .NET 3.5 CF application I would do the following:
public static List<string> GetAvailableProjectIds()
{
var idList = new List<string>();
var url = "the url";
var req = CreateRequest(url, true);
try
{
using (var response = (HttpWebResponse)req.GetResponse())
{
//do something with the resonse
}
}
catch (Exception ex)
{
}
return idList;
}
The Exception that is caught is a WebException and contains the correct 403 Forbiden Status Code. But nothing more helpful that I can find.
Is there a way to get at the Content property so I can display to the end user that they tried authenticating with an "Unauthorized User"?
I never really liked that behavior, that it uses exceptions when the communication was just fine. Try adding this extension method:
private static HttpResponse GetAnyResponse(this HttpRequest req)
{
HttpResponse retVal = null;
try
{
retVal = (HttpWebResponse)req.GetResponse()
}
catch (WebException webEx)
{
retVal = webEx.Response;
}
catch (Exception ex)
{
// these are the "bad" exceptions, let them pass
throw;
}
return webEx;
}
And then change your code to this:
using (var response = (HttpWebResponse)req.GetAnyResponse())
{
//do something with the resonse
}