ASP.MVC oferuje bardzo bogatą peletę klas, które można użyć jako zwracane typy w akcjach kontrolerów.

W moich projektach interfejs z HTML zwracany jest przeważnie raz dla konkretnego widoku. Następnie wykonywane są operacje wymiany danych w formacie JSON. Warto więc było włożyć na początku trochę więcej pracy aby dane były przekazywane w taki sam sposób we wszystkich akcjach.

Założyłem również, że zwrasana klasa będzie zbierała z modelu informacje o ewentualnych błędach i zwracała je w postaci komunikatów.

Zacznijmy więc od zwracanych komunikatów. Zakładam, że wystarczy typ i treść.

    /// 
    /// Typy komunikatów
    /// 
    public enum BaseMessageType { success = 0, error = 1, information = 2, warning = 3, none = 4}

    /// 
    /// Struktura zwracanego komunikatu
    /// 
    public class BaseAjaxMessage
    {
        /// 
        /// Typ komunikatu
        /// 
        public BaseAjaxMessage MessageType { get; set; }
        
        /// 
        /// Treść komunikatu
        /// 
        public string Message { get; set; }
    }

Kolej na wspólny typ odpowiedzi z obsługą sprawdzania stanu modelu.

    /// <summary>
    /// Typy odpowiedzi
    /// </summary>
    public enum BaseResponseType { Success = 0, Error = 1, Information = 2, Warning = 3, None = 4 }
 
    /// <summary>
    /// Odpowiedź
    /// </summary>
    public class BaseAjaxResponse
    {
        public BaseAjaxResponse()
        {
            this.Messages = new List<BaseAjaxMessage>();
        }
 
        /// <summary>
        /// Typ odpowiedzi
        /// </summary>
        public BaseResponseType ResponseType { get; set; }
 
        /// <summary>
        /// Lista komunikatów
        /// </summary>
        public List<BaseAjaxMessage> Messages { get; set; }
 
        /// <summary>
        /// Ustawienie przekazywanych danych
        /// </summary>
        /// <typeparam name="T">typ danych</typeparam>
        /// <param name="res">
        /// <param name="data">dane
        /// <returns></returns>
        public static BaseAjaxResponse<T> SetData<T>(BaseAjaxResponse res, T data) where T: class
        {
            return new BaseAjaxResponse<T>(data)
            {
                Messages = res.Messages,
                ResponseType = res.ResponseType,
            };
        }
 
        /// <summary>
        /// Sprawdzenie stanu modelu - dla kontrolerów MVC
        /// </summary>
        /// <param name="model">
        public void CheckModelState(ModelStateDictionary model)
        {           
            foreach (var val in model)
            {
                if (val.Value.Errors.Any())
                {
                    this.ResponseType = BaseResponseType.Error;
                    foreach (var err in val.Value.Errors)
                    {
                        this.AddAjaxMessage(err.ErrorMessage, BaseResponseType.Error, val.Key);
                    }
                }
            }
        }
 
        /// <summary>
        /// Sprawdzenie stanu modelu - dla kontrolerów WebApi
        /// </summary>
        /// <param name="model">
        public void CheckModelState(System.Web.Http.ModelBinding.ModelStateDictionary model)
        {
            foreach (var val in model)
            {
                if (val.Value.Errors.Any())
                {
                    this.ResponseType = BaseResponseType.Error;
                    foreach (var err in val.Value.Errors)
                    {
                        this.AddAjaxMessage(err.ErrorMessage, BaseResponseType.Error, val.Key);
                    }
                }
            }
        }
 
        /// <summary>
        /// Informacja czy ResponseType ma pozytywna wartosc (`Success`, `Information` lub `None`)
        /// </summary>
        public bool IsSuccess
        {
            get
            {
                switch (ResponseType)
                {
                    case BaseResponseType.Success:
                    case BaseResponseType.Information:
                    case BaseResponseType.None:
                        return true;
                    case BaseResponseType.Error:
                    case BaseResponseType.Warning:
                        return false;
                    default:
                        throw new InvalidOperationException("Nieznana wartosc BaseResponseType");
                }
            }
        }
    }

Na koniec dodajmy jeszcze typ generyczny aby móc przekazywać w zgrabny sposób dowolne struktury danych do naszej odpowiedzi.

    /// 
    /// Odpowiedź z danymi
    /// </summary>
    public class BaseAjaxResponse<T> : BaseAjaxResponse where T: class
    {
        public static implicit operator BaseJson<T>(BaseAjaxResponse<T> obj)
        {
            return new BaseJson<T>(obj);
        }

        /// <summary>
        /// Domyslny konstruktor
        /// </summary>
        public BaseAjaxResponse()
        {
            this.ResponseType = BaseResponseType.None;
        } 

        /// <summary>
        /// Konstruktor z ustawieniem danych
        /// </summary>
        /// <param name="data"></param>
        public BaseAjaxResponse(T data) : this()
        {
            this.Data = data;
        }

        /// <summary>
        /// Dane
        /// </summary>
        public T Data { get; set; }

        /// <summary>
        /// Sprawdzenie ModelState i dodanie bledow na liste
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public new BaseAjaxResponse<T> CheckModelState(ModelStateDictionary model)
        {
            base.CheckModelState(model);
            return this;
        }
    }

Teraz można cieszyć się spójną obsługą zwracanych danych we wszystkich akcjach kontrolerów.

[HttpGet]
public BaseJson<DataInfo> GetDataInf()
{
  BaseAjaxResponse<DataInfo> response = new BaseAjaxResponse<DataInfo>();
  if (ModelState.IsValid)
  {
    // zwracamy 'opakowane dane'
    response.Data = new DataInfo();
    // mozemy dodac komunikat
	response.AddAjaxMessage("Wszystko ok :)", BaseResponseType.Success);
  }
  else
    response.CheckModelState(ModelState);
  return response;
}