Перезаписать тело ответа в Owin Middleware

#c# #asp.net-web-api #owin

#c# #asp.net-веб-api #owin

Вопрос:

У меня есть класс Owin middleware, который я использую. Цель состоит в том, чтобы перезаписать тело ответа при обнаружении кода состояния HTTP 401, 403 или 405 и заменить тело объектом JSON. Пока это мой метод:

 public override async Task Invoke(IOwinContext context)
        {
            await Next.Invoke(context);

            if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403 || context.Response.StatusCode == 405)
            {

                var owinResponse = context.Response;
                var owinResponseStream = owinResponse.Body;
                var responseBuffer = new MemoryStream();
                owinResponse.Body = responseBuffer;

                string message;

                switch (context.Response.StatusCode)
                {
                    case 401:
                        message = "unauthorized request";
                        break;
                    case 403:
                        message = "forbidden request";
                        break;
                    default:
                        message = "request not allowed";
                        break;
                }
                var newResponse = new ResponseMessage<string>
                {
                    IsError = true,
                    StatusCode = (HttpStatusCode) Enum.Parse(typeof(HttpStatusCode), context.Response.StatusCode.ToString()),
                    Data = null,
                    Message = message
                };

                var customResponseBody = new StringContent(JsonConvert.SerializeObject(newResponse));
                var customResponseStream = await customResponseBody.ReadAsStreamAsync();
                await customResponseStream.CopyToAsync(owinResponseStream);
                owinResponse.ContentType = "application/json";
                owinResponse.ContentLength = customResponseStream.Length;
                owinResponse.StatusCode = 200;
                owinResponse.Body = owinResponseStream;
            }

        }
  

По большей части это работает, однако тело ответа добавляется вместо замены. Например, в случае ошибки 401 тело ответа является:

 {"message":"Authorization has been denied for this request."}
{"IsError":true,"StatusCode":401,"Data":null,"Message":"unauthorized request"}
  

вместо:

 {"IsError":true,"StatusCode":401,"Data":null,"Message":"unauthorized request"}
  

Я уверен, что это как-то связано с тем, как я пишу в тело ответа, но пока ничего не решило проблему.

Любые предложения будут высоко оценены.

Спасибо

Ответ №1:

Потому что вы пишете приведенный ниже код после await Next.Invoke(context);

 await Next.Invoke(context);

            if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403 || context.Response.StatusCode == 405)
            {

                var owinResponse = context.Response;
                var owinResponseStream = owinResponse.Body;
                var responseBuffer = new MemoryStream();
                owinResponse.Body = responseBuffer;
  

Это неправильно.

Следующий фрагмент кода будет работать так, как вы хотите:

 public override async Task Invoke(IOwinContext context)
        {
            var owinResponse = context.Response;
            var owinResponseStream = owinResponse.Body;
            var responseBuffer = new MemoryStream();
            owinResponse.Body = responseBuffer;

            await Next.Invoke(context);

            if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403 || context.Response.StatusCode == 405)
            {

                string message;

                switch (context.Response.StatusCode)
                {
                    case 401:
                        message = "unauthorized request";
                        break;
                    case 403:
                        message = "forbidden request";
                        break;
                    default:
                        message = "request not allowed";
                        break;
                }
                var newResponse = new ResponseMessage<string>
                {
                    IsError = true,
                    StatusCode = (HttpStatusCode) Enum.Parse(typeof(HttpStatusCode), context.Response.StatusCode.ToString()),
                    Data = null,
                    Message = message
                };

                var customResponseBody = new StringContent(JsonConvert.SerializeObject(newResponse));
                var customResponseStream = await customResponseBody.ReadAsStreamAsync();
                await customResponseStream.CopyToAsync(owinResponseStream);
                owinResponse.ContentType = "application/json";
                owinResponse.ContentLength = customResponseStream.Length;
                owinResponse.StatusCode = 200;
                owinResponse.Body = owinResponseStream;
            }

        }
  

Ответ №2:

Когда вы сначала устанавливаете тело ответа в поток памяти, курсор (текущая позиция ) переместится в его конец. owinResponse.Body = responseBuffer; в результате мы получили {"message":"Authorization has been denied for this request."} сохраненные и указывающие курсор точки в конце.

Опять же, в конце вашего метода вы пишете новый ответ owinResponse.Body = owinResponseStream; , который содержит сообщение {"IsError":true,"StatusCode":401,"Data":null,"Message":"unauthorized request"}

Поскольку текущая позиция указывает на конец потока, следовательно, она будет добавлена.

Попробуйте удалить первый набор тела owinResponse.Body = responseBuffer; поскольку вам не нужно исходное ответное сообщение.