gpt4 book ai didi

c# - 我应该如何从 .NET MVC 应用程序中的所有函数返回通用响应和响应代码?

转载 作者:行者123 更新时间:2023-11-30 17:42:41 24 4
gpt4 key购买 nike

我希望能够从我的 MVC 应用程序的业务层中的函数调用返回一个通用响应。大多数时候我看到一个对象创建函数看起来像这样

 public int Create(ICNUser item)
{
return this._repository.Create(item);
}
public void Update(ICNUser item)
{
this._repository.Create(item);
}

在这种情况下,_repository 是一个包装 Entity Framework 的存储库。

这在很多情况下都很有效,但我希望返回更多信息,并且我希望有一个成功/失败变量和一个响应代码来说明此操作验证失败的原因。我希望能够选择性地返回插入的对象或选定的对象。

一个示例是创建用户函数,该函数返回电子邮件不能为空错误和/或用户已存在错误,并且根据错误我向用户显示不同的消息。

我遇到的问题是我想让单元测试覆盖一个函数的所有可能的响应代码,而不必去查看代码并尝试弄清楚可能的返回值是什么。我正在做的事情感觉像是一种反模式。有没有更好的方法来完成这一切?

这就是我现在拥有的。

 public IGenericActionResponse<ICNUser> Create(ICNUser item)
{
return this._repository.Create(item);
}

public IGenericActionResponse Update(ICNUser item)
{
return this._repository.Update(item);
}

接口(interface)

 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 语句有点代码臭味。这向我建议您可能会受益于使用 subclassable enum .可子类枚举将映射 Action 响应代码或类型以返回带有模型的 View 。这是一个如何使用它的编译示例。

首先是我用来获取编译示例的一些类填充:

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

接下来是一些基础设施(框架)类(我使用 AtomicStack 项目中的 StringEnum 基类作为 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);
}
}

}

实现 SampleController:

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 并添加他们自己的额外枚举

使用可子类枚举的要点是利用 TrySelect 方法解析对特定枚举值的响应。然后我们对枚举值调用 GenerateView 方法以生成 View 。

枚举的另一个好处是,如果您需要根据枚举值做出其他决定,您只需向枚举添加另一个抽象方法,所有定义将被强制实现新的抽象方法,不同于传统的枚举/switch 语句组合,在传统枚举/switch 语句组合中,不需要添加新的枚举值,并且人们可能会忘记重新访问使用枚举的所有 switch 语句。

免责声明:我是 AtomicStack 项目的作者。如果您觉得它适合您的需要,请随意从项目中获取 Subclassable 枚举类代码。

更新:

如果你想注入(inject)响应枚举,你应该创建一个IResponseHandler适配器接口(interface)和一个GenerateViewForResponse类型的方法并提供一个具体的实现使用 ValidateHashResponses 枚举。

关于c# - 我应该如何从 .NET MVC 应用程序中的所有函数返回通用响应和响应代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31769130/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com