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; }