我应该如何返回一个通用的响应和响应代码从所有功能在一个.net MVC应用程序
本文关键字:一个 响应 应用程序 功能 代码 net MVC 何返回 返回 我应该 | 更新日期: 2023-09-27 18:05:50
我希望能够从MVC应用程序的业务层中的函数调用返回通用响应。大多数时候,我看到的对象创建函数是这样的
public int Create(ICNUser item)
{
return this._repository.Create(item);
}
public void Update(ICNUser item)
{
this._repository.Create(item);
}
在这种情况下,_repository是封装实体框架的存储库。
这对于很多情况都很有效,但我想要返回更多的信息,我想要有一个成功/失败变量和一个响应代码,说明为什么这个动作验证失败。我希望能够选择性地返回插入的对象或选定的对象。
一个例子是一个create user函数,它返回一个电子邮件不能是空白的错误或者一个用户已经存在的错误,基于这个错误我向用户显示一个不同的消息。
我遇到的问题是,我想让单元测试涵盖所有可能的响应代码,而不需要我去看代码,并试图找出可能的返回值是什么。我所做的感觉像是反模式。有没有更好的方法来完成这一切?
这是我现在拥有的。
public IGenericActionResponse<ICNUser> Create(ICNUser item)
{
return this._repository.Create(item);
}
public IGenericActionResponse Update(ICNUser item)
{
return this._repository.Update(item);
}
接口 namespace Web.ActionResponses
{
public enum ActionResponseCode
{
Success,
RecordNotFound,
InvalidCreateHash,
ExpiredCreateHash,
ExpiredModifyHash,
UnableToCreateRecord,
UnableToUpdateRecord,
UnableToSoftDeleteRecord,
UnableToHardDeleteRecord,
UserAlreadyExists,
EmailCannotBeBlank,
PasswordCannotBeBlank,
PasswordResetHashExpired,
AccountNotActivated,
InvalidEmail,
InvalidPassword,
InvalidPageAction
}
public interface IGenericActionResponse
{
bool RequestSuccessful { get; }
ActionResponseCode ResponseCode { get; }
}
public interface IGenericActionResponse<T>
{
bool RequestSuccessful { get; }
bool RecordIsNull{get;}
ActionResponseCode ResponseCode { get; }
}
}
实现namespace Web.ActionResponses
{
public class GenericActionResponse<T> : IGenericActionResponse<T>
{
private bool _requestSuccessful;
private ActionResponseCode _actionResponseCode;
public T Item { get; set; }
public GenericActionResponse(bool success, ActionResponseCode actionResponseCode, T item)
{
this._requestSuccessful = success;
this._actionResponseCode = actionResponseCode;
this.Item = item;
}
public GenericActionResponse(bool success, ActionResponseCode actionResponseCode)
{
this._requestSuccessful = success;
this._actionResponseCode = actionResponseCode;
this.Item = default(T);
}
public bool RecordIsNull
{
get
{
return this.Item == null;
}
}
public bool RequestSuccessful
{
get
{
return this._requestSuccessful;
}
}
public ActionResponseCode ResponseCode
{
get
{
return this._actionResponseCode;
}
}
}
public class GenericActionResponse : IGenericActionResponse
{
private bool _requestSuccessful;
private ActionResponseCode _actionResponseCode;
public GenericActionResponse(bool success, ActionResponseCode actionResponseCode)
{
this._requestSuccessful = success;
this._actionResponseCode = actionResponseCode;
}
public bool RequestSuccessful
{
get
{
return this._requestSuccessful;
}
}
public ActionResponseCode ResponseCode
{
get
{
return this._actionResponseCode;
}
}
}}
MVC应用public ActionResult ValidateResetHash(string passwordResetHash)
{
IGenericActionResponse result = (IGenericActionResponse)this._userManager.IsValidPasswordResetHash(passwordResetHash);
if (result.RequestSuccessful)
{
Models.PasswordChangeModel model = new Models.PasswordChangeModel();
model.PasswordResetHash = passwordResetHash;
return View("~/Areas/Public/Views/ResetPassword/PasswordChangeForm.cshtml", model);
}
else
{
switch (result.ResponseCode)
{
case ActionResponseCode.RecordNotFound:
{
FermataFish.Models.GenericActionModel responseModel = new FermataFish.Models.GenericActionModel(true, "/Login", "Login", "You have submitted an invalid password reset link.", false);
return View("~/Views/Shared/GenericAction.cshtml", responseModel);
}
case ActionResponseCode.PasswordResetHashExpired:
{
FermataFish.Models.GenericActionModel responseModel = new FermataFish.Models.GenericActionModel(true, "/ResetPassword", "Reset Password", "You have submitted an expired password reset link. You must reset your password again to change it.", false);
return View("~/Views/Shared/GenericAction.cshtml", responseModel);
}
default:
{
FermataFish.Models.GenericActionModel responseModel = new FermataFish.Models.GenericActionModel(true, "/", "Home", "An unknown error has occured. The system administrator has been notified. Error code:" + Enum.GetName(typeof(ActionResponseCode), result.ResponseCode), false);
return View("~/Views/Shared/GenericAction.cshtml", responseModel);
}
}
}
}
ValidateResetHash响应中的switch语句有点代码臭。这对我来说意味着你可以从子类枚举的使用中获益。子类枚举将映射动作响应代码或类型以返回带有模型的视图。下面是一个编译示例,说明如何使用它。
首先是一些类填充,我用来得到一个编译示例:
public class GenericActionModel
{
private bool v1;
private string v2;
private string v3;
private string v4;
private bool v5;
protected GenericActionModel() {}
public GenericActionModel(bool v1, string v2, string v3, string v4, bool v5)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
this.v4 = v4;
this.v5 = v5;
}
}
public class ActionResult
{
private GenericActionModel responseModel;
private string v;
public ActionResult(string v, GenericActionModel responseModel)
{
this.v = v;
this.responseModel = responseModel;
}
}
public class PasswordChangeModel : GenericActionModel
{
public object PasswordResetHash
{
get;
set;
}
}
public interface IUserManager
{
Response IsValidPasswordResetHash(string passwordResetHash);
}
下一步一些基础设施(框架)类(我使用StringEnum基类从AtomicStack项目的ResponseEnum基类):
public abstract class Response
{
public abstract string name { get; }
}
public class Response<TResponse> : Response where TResponse : Response<TResponse>
{
private static string _name = typeof(TResponse).Name;
public override string name => _name;
}
// Base ResponseEnum class to be used by more specific enum sets
public abstract class ResponseEnum<TResponseEnum> : StringEnum<TResponseEnum>
where TResponseEnum : ResponseEnum<TResponseEnum>
{
protected ResponseEnum(string responseName) : base(responseName) {}
public abstract ActionResult GenerateView(Response response);
}
以下是一些示例回答:
public class HashValidated : Response<HashValidated>
{
public string passwordResetHash;
}
public class InvalidHash : Response<InvalidHash> {}
public class PasswordResetHashExpired : Response<PasswordResetHashExpired> {}
public class Unexpected : Response<Unexpected> {}
映射样例响应的样例子类可枚举看起来像这样:
public abstract class ValidateHashResponses : ResponseEnum<ValidateHashResponses>
{
public static readonly ValidateHashResponses HashOk = HashValidatedResponse.instance;
public static readonly ValidateHashResponses InvalidHash = InvalidHashResponse.instance;
public static readonly ValidateHashResponses PasswordResetHashExpired = PasswordResetHashExpiredResponse.instance;
public static readonly ValidateHashResponses Default = DefaultResponse.instance;
private ValidateHashResponses(string responseName) : base(responseName) {}
protected abstract class ValidateHashResponse<TValidateHashResponse, TResponse> : ValidateHashResponses
where TValidateHashResponse : ValidateHashResponse<TValidateHashResponse, TResponse>, new()
where TResponse : Response<TResponse>
{
public static TValidateHashResponse instance = new TValidateHashResponse();
private static string name = Response<TResponse>.Name;
protected ValidateHashResponse() : base(name) {}
}
protected class HashValidatedResponse : ValidateHashResponse<HashValidatedResponse, HashValidated>
{
public override ActionResult GenerateView(Response response)
{
PasswordChangeModel model = new PasswordChangeModel();
model.PasswordResetHash = ((HashValidated) response).passwordResetHash;
return new ActionResult("~/Areas/Public/Views/ResetPassword/PasswordChangeForm.cshtml", model);
}
}
protected class InvalidHashResponse : ValidateHashResponse<InvalidHashResponse, InvalidHash>
{
public override ActionResult GenerateView(Response response)
{
GenericActionModel responseModel = new GenericActionModel(true, "/Login", "Login", "You have submitted an invalid password reset link.", false);
return new ActionResult("~/Views/Shared/GenericAction.cshtml", responseModel);
}
}
protected class PasswordResetHashExpiredResponse : ValidateHashResponse<PasswordResetHashExpiredResponse, PasswordResetHashExpired>
{
public override ActionResult GenerateView(Response response)
{
GenericActionModel responseModel = new GenericActionModel(true, "/ResetPassword", "Reset Password", "You have submitted an expired password reset link. You must reset your password again to change it.", false);
return new ActionResult("~/Views/Shared/GenericAction.cshtml", responseModel);
}
}
protected class DefaultResponse : ValidateHashResponses
{
public static DefaultResponse instance = new DefaultResponse();
private DefaultResponse() : base("Default") {}
public override ActionResult GenerateView(Response response)
{
GenericActionModel responseModel = new GenericActionModel(true, "/", "Home", "An unknown error has occured. The system administrator has been notified. Error code:" + response.name, false);
return new ActionResult("~/Views/Shared/GenericAction.cshtml", responseModel);
}
}
}
实现samplcontroller:
public class SampleController
{
private IUserManager _userManager;
public ActionResult ValidateResetHash(string passwordResetHash)
{
Response result = this._userManager.IsValidPasswordResetHash(passwordResetHash);
var resultType = ValidateHashResponses.TrySelect(result.name,ValidateHashResponses.Default);
return resultType.GenerateView(result);
}
}
调整上面的代码以适应您的情况。
如果你想允许其他人扩展 ValidateHashResponses枚举,你可以将构造函数设置为protected而不是private。然后,他们可以扩展ValidateHashResponses并添加自己的附加enum 值。
使用可子类枚举的意义在于,它可以利用TrySelect方法来解析对特定枚举值的响应。然后在枚举值上调用GenerateView方法来生成视图。
枚举的另一个好处是,如果您需要根据枚举值做出其他决定,您只需向枚举添加另一个抽象方法,所有值定义将强制实现新的抽象方法,这与传统的enum/switch语句组合不同,在传统的enum/switch语句组合中,不需要添加新枚举值,并且可能会忘记重新访问使用枚举的所有switch语句。
免责声明:我是AtomicStack项目的作者。如果您觉得适合您的需要,可以随意从项目中获取Subclassable enum类代码。
:
如果你想注入响应枚举,你应该用GenerateViewForResponse
类型的方法创建一个IResponseHandler
适配器接口,并提供一个使用ValidateHashResponses枚举的具体实现。