09 декабря 2020

DelphiMVCFramework. Собственный обработчик ошибок

    Осенью я написал на Delphi небольшой REST-сервис для обмена данными с информационной системой другой компании. По регламенту информационного взаимодействия ответ на HTTP-запрос с ошибкой должен быть в формате JSON:
{
  "error_code": код ошибки,
  "error_name": "текст ошибки"
}
    Для начала приведу примеры ответов моего сервиса, которые возвращал сам DelphiMVCFramework:
1. При вызове несуществующего метода:
Пусть вас не смущает, что для GET-метода у меня в API используется метод POST – это требование регламента взаимодействия. Вероятно, что в компании, сочинившей регламент, знают только этот метод. С другой стороны, для REST API не существует официального стандарта, поэтому можно делать как захочется.
2. При вызове метода с не правильным паролем:
Как вы видите, эти ответы совсем не подходят под требования регламента. Что бы исправить это нам необходимо добавить собственный обработчик ошибок и в нем сформировать правильный ответ. Собственный обработчик ошибок в DelphiMVCFramework – это процедура:
TMVCExceptionHandlerProc = reference to procedure(E: Exception; SelectedController: TMVCController; WebContext: TWebContext; var ExceptionHandled: Boolean);
которая устанавливается, как обработчик ошибок для экземпляра объекта TMVCEngine. В примере к библиотеке "custom_exception_handling" это сделано так:
type
  TMyWebModule = class(TWebModule)
    ...
  private
    FMVC: TMVCEngine;
  end; 
...
procedure TMyWebModule.WebModuleCreate(Sender: TObject);
var
  lExceptionHandler: TMVCExceptionHandlerProc;
begin
  lExceptionHandler := procedure(
      E: Exception;
      SelectedController: TMVCController;
      WebContext: TWebContext;
      var ExceptionHandled: Boolean)
    var
      lColor: string; 
    begin
      if E is EMyException then
      begin
        case EMyException(E).Severity of
          Fatal, Error:
            lColor := 'red';
          Warning:
            lColor := 'yellow';
          Information:
            lColor := 'blue';
        else
          lColor := 'black';
        end;
        WebContext.Response.ContentType := TMVCMediaType.TEXT_HTML;
        WebContext.Response.Content :=
          '<html><body><h1>Error occurred</h1>' +
          Format('<h2 style="color: %s">', [lColor]) + 
                 TNetEncoding.HTML.Encode(EMyException(E).ToString) + 
          '</h2>' + '<p>your truly custom exception handler...</p>' +
          '</body></html>';
        ExceptionHandled := True;
      end
      else if E is EMVCException then
      begin
        WebContext.Response.ContentType := TMVCMediaType.TEXT_HTML;
        WebContext.Response.Content :=
          '<html><body><h1>Error occurred</h1>' +
          Format('<h2 style="color: red">', [lColor]) + 
                 TNetEncoding.HTML.Encode(E.Message) + '</h2>' +
          '<p>your truly custom exception handler...</p>' +
          '</body></html>';
        ExceptionHandled := True;
      end;
    end;

  FMVC := TMVCEngine.Create(Self,
    procedure(Config: TMVCConfig)
    begin
      ...
    end);
  FMVC.SetExceptionHandler(lExceptionHandler);
Моя реализация процедуры обработчика ошибок выглядит так:
procedure ExceptionHandlerProc(E: Exception; SelectedController: TMVCController;
                               WebContext: TWebContext; var ExceptionHandled: Boolean);
  procedure CreateResponse(const iErrorCode: Integer; const sErrorName: String);
  var
    jo: TJSONObject;
  begin
    WebContext.Response.StatusCode := iErrorCode;
    jo := TJSONObject.Create;
    try
      jo.AddPair('error_code', TJSONNumber.Create(iErrorCode));
      jo.AddPair('error_name', sErrorName);
      WebContext.Response.Content := jo.ToString;
    finally
      jo.Free
    end;
  end;
begin
  WebContext.Response.ContentType := TMVCMediaType.APPLICATION_JSON;

  if E is EXyzException
    then CreateResponse(EXyzException(E).Code, EXyzException(E).LogMessage)
    else if E is EMVCException
           then CreateResponse(EMVCException(E).HttpErrorCode, E.Message)
           else CreateResponse(500, E.Message);
           
  ExceptionHandled := True;
end;
Теперь посмотрим содержимое ответов моего сервиса с ее использованием:
1. При вызове несуществующего метода:
2. При вызове метода с не правильным паролем:
DelphiMVCFramework - RESTful, JSON-RPC и ActiveRecord фреймворк для разработки на Delphi.

2 комментария:

  1. А чем DelphiMVCFramework удобнее того же банального TIdHTTPServer?

    ОтветитьУдалить
    Ответы
    1. У TIdHTTPServer надо самому допиливать многие плюшки, которые в DMVCF уже реализованы. Посмотрите демки.

      Удалить