我在 ASP.NET MVC 5 中有這個 Action 方法:
namespace LDAPMVCProject.Controllers
{
public class HomeController : Controller
{
public ActionResult UsersInfo(string username, string password)
{
DomainContext result = new DomainContext();
try
{
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
string ADServerName = System.Web.Configuration.WebConfigurationManager.AppSettings["ADServerName"];
string ADusername = System.Web.Configuration.WebConfigurationManager.AppSettings["ADUserName"];
string ADpassword = System.Web.Configuration.WebConfigurationManager.AppSettings["ADPassword"];
using (var context = new DirectoryEntry("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword))
using (var search = new DirectorySearcher(context))
{
// validate username & password
using (var context2 = new PrincipalContext(ContextType.Domain, "mydomain.com", ADusername, ADpassword))
{
bool isvalid = context2.ValidateCredentials(username, password);
if !(isvalid)
return **** // authentication error
}
// create search object which operates on LDAP connection object
// and set search object to only find the user specified
// DirectorySearcher search = new DirectorySearcher(myLdapConnection);
// search.PropertiesToLoad.Add("telephoneNumber");
search.Filter = "(&(objectClass=user)(sAMAccountName=test.test))";
// create results objects from search object
// user exists, cycle through LDAP fields (cn, telephonenumber etc.)
SearchResult r = search.FindOne();
ResultPropertyCollection fields = r.Properties;
foreach (String ldapField in fields.PropertyNames)
{
if (ldapField.ToLower() == "telephonenumber")
{
foreach (Object myCollection in fields[ldapField])
{
result.Telephone = myCollection.ToString();
}
}
else if (ldapField.ToLower() == "department")
{
foreach (Object myCollection in fields[ldapField])
{
result.Department = myCollection.ToString();
}
}
// }
}
if (result.Telephone == null)
return ***** //Telephone is empty
if (result.Department)
return **** // department is empty
string output = JsonConvert.SerializeObject(result);
return Content(output, "application/json");//success
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" e.ToString());
}
return View(result);
}
}
}
action 方法充當我們 Web 應用程式的 API 端點,其中 API 接受用戶名和密碼,并執行以下操作:
根據 Active Directory 驗證用戶名/密碼
如果有效;檢查電話號碼是否為空 >> 如果是則回傳錯誤
如果有效;檢查部門是否為空 >> 如果是則回傳錯誤
如果有效并找到資訊;為用戶回傳部門和電話
現在我對如何回傳前 3 點的 JSON 有點困惑?我是否應該始終回傳帶有狀態訊息的 http 200(狀態:“成功”或狀態:“失敗”)?或者如果用戶名/密碼驗證失敗,那么我應該回傳 http 401 而不必回傳任何 JSON 內容?
誰能幫我這個?
我需要以標準方式撰寫操作方法,以供 3rd 方應用程式使用。
第二個問題:如果代碼引發例外,我需要回傳什么?
謝謝
uj5u.com熱心網友回復:
這是一種 API 錯誤處理和日志記錄設計,以下型別的方法效果很好,可以分離關注點并保持主邏輯干凈:
設計錯誤回應
這些應該對客戶有用,例如,如果他們需要顯示錯誤或基于特定原因做某事。4xx 錯誤可能包含此有效負載以及 HTTP 狀態:
{
"code": "authentication_failed",
"message": "Invalid credentials were provided"
}
根據在這種情況下 UI 將顯示的內容以及您在日志中查找錯誤的方式,通常會為 500 錯誤提供不同的有效負載:
{
"code": "authentication_error",
"message": "A problem was encountered during a backend authentication operation",
"area": "LDAP",
"id": 12745,
"utcTime": "2022-07-24T10:27:33.468Z"
}
設計 API 日志
在第一種情況下,服務器日志可能包含以下欄位:
{
"id": "7af62b06-8c04-41b0-c428-de332436d52a",
"utcTime": "2022-07-24T10:27:33.468Z",
"apiName": "MyApi",
"operationName": "getUserInfo",
"hostName": "server101",
"method": "POST",
"path": "/userInfo",
"errorData": {
"statusCode": 401,
"clientError": {
"code": "authentication_failed",
"message": "Invalid credentials were provided",
"context": "The account is locked out"
}
}
}
在第二種情況下,服務器日志可能包含以下欄位:
{
"id": "7af62b06-8c04-41b0-c428-de332436d52a",
"utcTime": "2022-07-24T10:27:33.468Z",
"apiName": "MyApi",
"operationName": "getUserInfo",
"hostName": "server101",
"method": "POST",
"path": "/userInfo",
"errorData": {
"statusCode": 500,
"clientError": {
"code": "authentication_error",
"message": "A problem was encountered during a backend authentication operation",
"area": "LDAP",
"id": 12745,
"utcTime": "2022-07-24T10:27:33.468Z"
},
"serviceError": {
"details": "Host not found: error MS78245",
"stack": [
"Error: An unexpected exception occurred in the API",
"at DirectorySearcher: 871 ... "
]
}
}
代碼
也許旨在使用與此類似的代碼來表示您想要的錯誤和日志記錄行為。ClientError
和類啟用上述ServiceError
回應和日志。當拋出錯誤時,這應該使您能夠添加有用的背景關系資訊:
public class HomeController : Controller
{
public ActionResult UsersInfo(string username, string password)
{
DomainContext result = new DomainContext();
try
{
DirectoryEntry myLdapConnection = createDirectoryEntry();
string ADServerName = System.Web.Configuration.WebConfigurationManager.AppSettings["ADServerName"];
string ADusername = System.Web.Configuration.WebConfigurationManager.AppSettings["ADUserName"];
string ADpassword = System.Web.Configuration.WebConfigurationManager.AppSettings["ADPassword"];
using (var context = new DirectoryEntry("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword))
using (var search = new DirectorySearcher(context))
{
using (var context2 = new PrincipalContext(ContextType.Domain, "mydomain.com", ADusername, ADpassword))
{
bool isvalid = context2.ValidateCredentials(username, password);
if !(isvalid)
throw new ClientError(401, "authentication_failed", "Invalid credentials were provided", "optional context goes here");
}
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(&(objectClass=user)(sAMAccountName=test.test))";
SearchResult r = search.FindOne();
ResultPropertyCollection fields = r.Properties;
foreach (String ldapField in fields.PropertyNames)
{
if (ldapField.ToLower() == "telephonenumber")
{
foreach (Object myCollection in fields[ldapField])
{
result.Telephone = myCollection.ToString();
}
}
else if (ldapField.ToLower() == "department")
{
foreach (Object myCollection in fields[ldapField])
{
result.Department = myCollection.ToString();
}
}
}
if (result.Telephone == null)
throw new ClientError(400, "invalid_user_data", "User data is invalid", "Telephone is missing");
if (result.Department)
throw new ClientError(400, "invalid_user_data", "User data is invalid", "Department is missing");
string output = JsonConvert.SerializeObject(result);
return Content(output, "application/json");
}
}
catch (Exception e)
{
throw new ServiceError("authentication_error", "A problem was encountered during a backend authentication operation", "LDAP", e);
}
return View(result);
}
}
中間件
通常的模式是使用小型中間件類來處理例外處理、回傳錯誤回應和寫入錯誤日志:
- 日志過濾器
- 例外過濾器
此處撰寫的邏輯型別在一定程度上取決于您的偏好,但可能類似于以下內容:
public class ErrorFilterAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var logEntry = new ErrorLogEntry();
var jsonResponse = ""
var statusCode = 500;
if (filterContext.Exception is ClientError)
{
var clientError = filterContext.Exception as ClientError;
logEntry.AddClientErrorDetails(clientError);
statusCode = clientError.StatusCode;
jsonResponse = clientError.toResponseFormat();
}
if (filterContext.Exception is ServiceError)
{
var serviceError = filterContext.Exception as ServiceError;
logEntry.AddServiceErrorDetails(serviceError);
statusCode = serviceError.StatusCode;
jsonResponse = serviceError.toResponseFormat();
}
logEntry.Write();
filterContext.Result = new JsonResult(jsonResponse);
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = statusCode;
filterContext.ExceptionHandled = true;
}
}
uj5u.com熱心網友回復:
有很多方法可以解決這個問題,最終您希望您的端點以任何使用您的端點的人所期望的方式運行。
我偶然發現這是一種有趣的方式來處理對您的端點的請求中的細微差別。即使這用于 Graph API,您也可以根據需要使用該概念。https://developers.facebook.com/docs/graph-api/guides/error-handling。TL;DR 將具有標準化的 json 回應,例如:
{
"error": {
"message": "Message describing the error",
"type": "OAuthException",
"code": 190,
"error_subcode": 460,
"error_user_title": "A title",
"error_user_msg": "A message",
"fbtrace_id": "EJplcsCHuLu"
}
}
uj5u.com熱心網友回復:
HTTP 狀態代碼非常靈活,可能會混淆何時使用什么。我的建議:
- 識別 Http 狀態族(X00)
- 100s:資訊代碼:服務器確認瀏覽器發起的請求并且正在處理(100-199)。
- 200s:成功代碼:請求接收、理解、處理和預期的資訊中繼到瀏覽器(200-299)。
- 300s:重定向代碼:不同的目的地已被請求的資源替代;可能需要瀏覽器采取進一步行動 (300–399)。
- 400s:客戶端錯誤代碼:未訪問網站或頁面;頁面不可用或請求存在技術問題 (400–499)。
- 500s:服務器錯誤代碼
- 在此處搜索您的回應(2XX) 的特定 Http 狀態代碼,這里有一些 200 系列的示例:
- 201:已創建。請求已完成;創建了新資源。POST 請求后的典型回應。
- 202:接受。瀏覽器請求已接受,仍在處理中。可能成功,也可能不成功。
對于您的示例,我將回傳:
- 403:禁止 - 如果用戶憑據錯誤。
- 200:好的 - 如果一切正常(所有資訊都回傳)。
當用戶通過身份驗證但沒有有效資料時,另一個選項有點棘手。你可以回傳:
- 204: No content - 因為用戶是auth但沒有資料
- 500: internal server error - 因為服務器不能回傳請求的物件
- 404:未找到 - (不是我個人的選擇,但它是一個選項)
它還取決于您的客戶和您的偏好。
快樂的科丁 :)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/519791.html
上一篇:如何使用pandasapply()有效地呼叫Web服務
下一篇:SEI和WSDL有什么區別