65.9K
CodeProject 正在变化。 阅读更多。
Home

提前了解 REST

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2017 年 3 月 15 日

Apache

5分钟阅读

viewsIcon

9556

在客户端,我希望在登录失败时获取并显示适当的消息(例如:电子邮件或密码不正确)。如何用 REST 实现?

在哪里可以找到 REST 的基础知识?

有关 REST 的基础知识,请访问

在客户端,我希望在登录失败时获取并显示适当的消息(例如:电子邮件或密码不正确)。如何用 REST 实现?

请将此方法添加到 UsersController 中,如下所示:

[HttpPost]
[Route("login")]
[ValidationActionFilter()]
public IHttpActionResult Login(LoginRequest request) {
	/*Verify username and pwd and throw exception if invalid*/
	throw new ValidationException("users.login.invalidUserNameOrPwd");
}

在此代码中,我们强制 API 始终向客户端返回“users.login.invalidUserNameOrPwd”验证错误。

当调用“<root api>/users/login”时

响应如下:

我期望收到明确的错误消息(例如:电子邮件或密码不正确),但 API 返回“users.login.invalidUserNameOrPwd”作为异常的键。你可能误解了我的意思,对吗?

不,我理解你的期望。

在这种情况下,我们应该避免向最终用户发送明确的错误消息,因为 API 可以服务于多种类型的设备和多种语言。

因此,API 返回键,客户端将在其设备上以适当的语言将该键翻译成适当的消息。这让 API 专注于应用程序的业务,而不是花费时间将消息键翻译成当前用户的适当语言。

网上有人说,登录失败时应该使用 401(未授权)。在这种情况下,我们应该使用它吗?

不,不应该。

401(未授权)应该在客户端想要在没有适当凭据的情况下获取用户配置文件(受限资源)的情况下使用。
是的,在这种情况下我们将返回 401(未授权)。
在我们的例子中,用户没有提供足够的信息来进行登录操作。

好的,那么,在这种情况下我应该使用哪个错误代码(HTTP 状态码)?

我倾向于使用“Bad Request”(400),因为此 HTTP 状态码旨在用于请求未提供足够信息来执行服务器上预期操作的情况。

我明白了。上面 Login 函数的“ValidationActionFilter”有什么作用?

这是 .NET 中的一个属性。仔细查看“ValidationActionFilter”属性的代码,我们有

public class ValidationActionFilter : ActionFilterAttribute
{
	public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
	{
		if (actionExecutedContext.Exception == null) {
			return;
		}
		if (actionExecutedContext.Exception is ValidationException) {
			actionExecutedContext.Response = actionExecutedContext.Request
				 .CreateResponse(HttpStatusCode.BadRequest, ((ValidationException)actionExecutedContext.Exception).Errors);
		}
	}
}

在此代码中,我们重写了“OnActionExecuted”方法。该方法将在函数执行后被调用。

在该函数中,我们检查适当函数的执行结果是否抛出任何 ValidationException 异常(使用 actionExecutedContext.Exception is ValidationException)。然后创建适当的响应并以 Bad Request (400) 返回给调用者/客户端。

我认为我们可以在 Login 函数中进行 try/catch 并按预期处理异常情况。

是的,我们可以。但是 try/catch 块会在所有函数中重复。这不好。

应用程序中通常有多少种错误/异常?

在我看来,通常有两种类型:

系统错误/异常

应用程序错误/异常。

我们通常以不同的方式处理这些类型的错误/异常。

你能详细解释一下系统错误吗?

它们是与网络、基础设施等相关的错误。

这意味着在这种情况下,调用者的请求还没有到达你的应用程序。例如:Not Found (404),.....

对于这些情况,我们可以显示错误页面或向用户显示错误消息。这对所有情况都一样。

那么,应用程序错误又如何呢?

从不同的角度来看,这与应用程序的逻辑有关。当调用者发送带有对适当逻辑无效的数据的请求到服务器时。例如:使用空的电子邮件/用户名登录。

在这种情况下,我们需要让调用者知道这些错误,并且它可以在登录表单上显示适当的错误。大多数验证错误都是应用程序错误/异常。

为了更清楚,你能给我一个用户登录系统时这两种错误的例子吗?

当然,让我们看看,用户在登录表单中输入他们的电子邮件/密码,并将请求发送到服务器,URI 为“<api root>/users/login”。

如果该 URI(“<api root>/users/login”)在服务器上不存在。这意味着服务器端未配置为处理此类型的请求。因此,.Net Framework 无法将你的请求映射到服务器上的适当操作。此错误是系统错误,因为在这种情况下你的应用程序代码未被执行。

否则,URI 存在并且调用了 UsersController 的 Login 方法。然后我们验证请求,发现电子邮件为空,但在此情况下是必需的。然后抛出异常。

在这种情况下,这是应用程序错误,因为该错误是由应用程序因数据不满足要求而抛出的。

调用者如何知道他们在调用我的 API 时收到了系统错误?

对于这些系统错误,服务器通常会响应特定的 HTTP 状态(例如:404:Not Found,...)。因此,调用者可以根据此来了解他们收到了哪种类型的错误。

应用程序错误/异常怎么样?

对于应用程序错误,我们应该返回带有 BadRequest (400) 的响应,并在响应正文中附加更多错误详细信息,如下图所示。

以及响应正文

好的,你能给我展示一下如何在 Web API 中实现这个吗?

请看下面的简单代码:

public HttpResponseMessage Login(LoginRequest request) {
	try
	{
		/*Verify username and pwd and throw exception if invalid*/
		throw new ValidationException("users.login.invalidUserNameOrPwd");
	}
	catch (ValidationException ex) {
		return Request.CreateResponse(HttpStatusCode.BadRequest, ex.Errors);
	}
}

我注意到在失败/成功的情况下,错误/数据返回的方式不同。这要求客户端在处理来自服务器的响应之前进行仔细检查。有没有其他解决方案?

这是大多数 REST API 的常见问题。

为了避免这种情况,我们需要包装响应结果。这样,失败/成功两种情况下的响应格式将是相同的。

看下面的代码:

[HttpPost]
[Route("login")]
public HttpResponseMessage Login(LoginRequest request)
{
	ResponseData<bool> response = new ResponseData<bool>();
	try
	{
		/*Assume that we will throw exception in the case client send request ="exception"*/
		if (request != null && request.Email == "exception")
		{
			throw new ValidationException("users.login.invalidUserNameOrPwd");
		}
		else {
			response.SetData(true);
		}
	}
	catch (ValidationException ex)
	{
		response.SetError(ex.Errors);
	}
	return Request.CreateResponse(response.HasError() ? HttpStatusCode.OK : HttpStatusCode.BadGateway, response);
}

在此代码中,我们始终向客户端返回 ResponseData 对象,它是 JSON 格式,我们知道它有多少个字段。

在成功的情况下,响应数据中的 errors 字段为 null/empty,因此我们可以从“data”字段获取数据并执行适当的操作,如下所示。

否则,在失败的情况下,只需获取“errors”字段中的错误列表并通知最终用户。

我们如何区分系统错误和应用程序错误?

我们可以基于“HTTP 状态码”来做到这一点。

在调用服务器时发生异常/错误的情况下,检查响应中是否有“errors”字段。

  • 如果有,则是应用程序错误。只需从“errors”字段获取错误列表并执行适当的操作。
  • 如果没有,这是系统错误。

我明白了,我们在每个函数中都有 try/catch 块来处理异常情况。正如本文所述,我们可以通过使用“ValidationActionFilter”来移除它,对吗?

是的,这是正确的。

感谢阅读。

注意:如果您认为这篇文章很有用,请点赞并分享给您的朋友,我将不胜感激。
© . All rights reserved.