Reputation: 144
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 there a better way to return a custom response object ('Result' in my case) that is serialized as the client would expect?
Upvotes: 3
Views: 4117
Reputation: 144
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();
}
Upvotes: 3