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