在.NET 6.0中自定义接口路由

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在.NET 6.0中自定义接口路由相关的知识,希望对你有一定的参考价值。

大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进。

在本文中,我们将讨论ASP.NET Core中的新路由。我们将了解什么是接口(endpoints)路由,它是如何工作的,它在哪里使用,以及如何创建自己的路由。

本文主题:

  • 探索接口路由
  • 创建自定义接口
  • 创建更复杂的接口

名词定义:端点,即我们访问的接口或者叫API,有些地方叫EndPoint或者叫接口,其实源头的称呼应该叫端点会更贴切一些。或者你也可以直接叫EndPoint,但是先不管那么多,大概了解这个意思就可以了。

探索接口路由

要了解接口路由(End Point),您需要了解什么是端点以及什么是路由。

端点是应用程序的一部分,当路由将传入的请求映射到它时,端点就会被执行。

客户端通常从服务器请求资源。大多数情况下,客户端是一个浏览器。资源由指向特定目标的URL定义。除了网页,它也可以是一个移动应用程序,从Web API请求特定JSON数据。

另一方面,执行的端点被映射到一个特定的路由,ASP.NET Core开发人员已经熟悉这样一种路由模式:

app.UseRouting(); 
app.UseAuthorization(); 
app.UseEndpoints(endpoints =>     
   endpoints.MapControllerRoute(name: "default", pattern: "controller=Home/action=Index/id?"); 
);

如果路由或路由模式与传入请求的URL匹配,则请求将映射到该端点。
ASP.NET Core可以映射到以下端点:

  • Controllers (例如, MVC or web APIs)
  • Razor Pages
  • SignalR (and Blazor Server)
  • gRPC services
  • Health checks
    大多数端点都有非常简单的路由模式。只有MVC和Web API端点使用更复杂的模式。Razor页面的路由定义基于实际页面的文件夹和文件结构。

在ASP.NET Core 2.2中引入端点之前,路由只是运用在MVC和Web API中。Razor Pages中的隐式路由是内置的,SignalR没有路由一说。Blazor和gRPC在当时还不不知道在哪儿,健康检查最初是作为中间件组件实现的。

引入端点路由是为了将路由与实际端点分离,说得白话一点,就是让URL地址和实际执行的Action进行分离,这会让框架更加灵活,同时这意味着新的端点不需要实现自己的路由。

创建自定义接口

创建端点的最简单方法是使用lambda:

app.Map("/map", async context =>   
    await context.Response.WriteAsync("OK"); 
);

这里将/map路由映射到一个简单的端点,该端点将单词“OK”写入响应流。

关于早期.NET 6.0版本的说明
.NET 6.0之前,该映射只能在Startup.cs文件中的UseEndpoints方法中,而使用.NET 6.0和新的Minimal API方法,可以在Program.cs文件中完成映射。

另外,我们需要将Microsoft.AspNetCore.Http命名空间添加到using语句中。
还可以将特定的HTTP方法(如GET、POST、PUT和DELETE)映射到端点。以下代码显示了如何映射GET和POST方法:

app.MapGet("/mapget", async context =>      
    await context.Response.WriteAsync("Map GET"); 
); 
app.MapPost("/mappost", async context =>   
        await context.Response.WriteAsync("Map POST");
);

我们还可以将两个或多个HTTP方法映射到一个端点:

app.MapMethods("/mapmethods",  new[]  "DELETE", "PUT" , 
    async context => 
        await context.Response.WriteAsync("Map Methods");
);

这些端点映射很像我们在第8篇定制.NET 6.0的Middleware中间件中看到的基于lambda的中间件组件,这些管道中间件会返回结果,例如基于HTML的视图、JSON结构化数据或类似的内容。但是,端点路由是一种更灵活的输出方式,它应该会在ASP.NET Core 3.0以后的所有版本中进行使用。

在第8篇中,我们看到我们可以像这样的分支管道:

app.Map("/map", mapped =>      // ……  );

以上这种方式也会创建一个路由,但只会侦听以/map开头的URL。如果您希望有一个处理/map/{id:int?}等模式的路由引擎,来匹配/map/456而不是/map/abc,那么您应该使用前面所述的新的路由。

而那些基于lambda的端点映射,对于简单的场景非常有用。然而,由于它们是在Program.cs中定义的,如果您想使用这种方式来实现更复杂的场景,代码维护性将变得很差。

因此,我们应该尝试找到一种更结构化的方法来创建自定义端点。

创建更复杂的接口

接下来,我们将创建一个健康检查接口例子,有点类似于您在Kubernetes集群中运行应用程序时可能需要的接口,用来检测系统的健康状态:
我们从开发者的角度定义API接口,我们首先添加一个MapMyHealthChecks方法,作为IEndpointRouteBuilder对象上的一个扩展方法,它没有实现:

app.MapMyHealthChecks("/myhealth");   
app.MapControllerRoute(name: "default",pattern:"controller=Home/action=Index/id?");

为了避免混淆,我们这儿采取和之前类似的方式添加新接口,后面我们进一步来实现一下。
我们创建一个名为MapMyHealthChecksExtensions的静态类,并在MapMyHealthCheck中放置一个扩展方法,该对象扩展IEndpointRouteBuilder接口并返回IEndpointConventionBuilder对象:

namespace RoutingSample; 
public static class MapMyHealthChecksExtensions      
    public static IEndpointConventionBuilder  MapMyHealthChecks (this IEndpointRouteBuilder endpoints, string pattern = "/myhealth")     
             
        // ...     
     

以上只是骨架,实际的接口将被实现为一个终止的中间件,也就是说,它不调用下一个的中间件组件,并创建响应流的输出:

namespace RoutingSample; 
public class MyHealthChecksMiddleware      
    private readonly ILogger _logger;     
    public MyHealthChecksMiddleware (RequestDelegate next, ILogger logger)    
       
        _logger = logger;     
         

    public async Task Invoke(HttpContext context)              
        // add some checks here...         
        context.Response.StatusCode = 200;         
        context.Response.ContentType = "text/plain";         
        await context.Response.WriteAsync("OK");     
     

实际工作是在Invoke方法中完成的。目前,只演示200状态码和OK状态响应,我们可以在这里随意扩展该方法,例如检查数据库或相关服务的可用性。

接下来我们使用这个终止中间件,我们回到MapMyHealthChecks方法的框架。我们现在创建一个自己的管道,并将其映射到给定的pipeline:

var pipeline = endpoints.CreateApplicationBuilder().UseMiddleware().Build(); 
return endpoints.Map(pattern, pipeline).WithDisplayName("My custom health checks");

这种方法允许我们为这个新的管道添加更多的中间件。WithDisplayName扩展方法将配置的显示名称设置为接口,接下来按F5键启动程序,并在浏览器中调用https://localhost:7111/myhealth。我们将看到:

请注意,端口号可能会有所不同。我们还可以将已经存在的终止中间件组件转换为路由接口,以配置更加灵活的路由。

总结

ASP.NET Core支持请求处理并向请求提供信息的多种方法。接口路由是一种基于URL和请求的方法提供资源。
在本文,我们学习了如何使用终止中间件组件作为接口,并用将该接口映射到新的路由引擎,从而让我们的路由变得更加强大和灵活。
每个Web应用程序都需要了解系统用户,以允许或限制对特定数据的访问。在下一章中,我们将展示如何配置身份验证和识别用户。

在 ASP.NET 中接受信用卡的最佳方式是啥? (在 ASP.NET 和 Authorize.NET 之间)

【中文标题】在 ASP.NET 中接受信用卡的最佳方式是啥? (在 ASP.NET 和 Authorize.NET 之间)【英文标题】:What is the best way to accept a credit card in ASP.NET? (Between ASP.NET and Authorize.NET)在 ASP.NET 中接受信用卡的最佳方式是什么? (在 ASP.NET 和 Authorize.NET 之间) 【发布时间】:2011-04-01 07:18:51 【问题描述】:

我是创建商务网站的新手,现在我需要通过互联网销售软件,我不知道从哪里开始。

我正在使用 ASP.NET,并且正在考虑使用 Authorize.NET 来验证和处理信用卡。

我正在寻找可以安装在单台服务器上的稳定、值得信赖的解决方案。我的第二个目标(除了在线销售产品)是熟悉流行的购物车软件,并被大型企业使用。也许我应该从 MS Commerce 服务器开始?

【问题讨论】:

与 Authorize.Net 集成非常简单。 【参考方案1】:

这里有一百万个选项,但如果您正在编写代码,最简单的代码方式是使用http://sharpauthorize.com/

【讨论】:

【参考方案2】:

Authorize.Net 很容易用 ASP.NET 实现

基本上你可以通过3-4种方式进行交易:

    通过按钮(如 Paypal (http://developer.authorize.net/api/simplecheckout/))进行简单结帐 Direct Post:假设您的定制比 Simple CheckOut 多一点。创建一个直接发布到 Authorize.Net http://developer.authorize.net/api/simplecheckout/ 的结帐表单

例如:

<h1><%=ViewData["message"] %></h1>
<%using (Html.BeginSIMForm("http://YOUR_SERVER.com/home/sim",
1.99M,"YOUR_API_LOGIN","YOUR_TRANSACTION_KEY",true))%>
<%=Html.CheckoutFormInputs(true)%>
<%=Html.Hidden("order_id","1234") %>
<input type = "submit" value = "Pay" />
<%%>
    SIM(服务器集成) AIM(高级集成方法):提供完全控制和自定义。 CIM(使用tokanization 将客户卡号和信息存储在Auth.NET 服务器上)

*下面是一个 CIM 函数进行交易的示例,AIM 与 CIM 非常相似,唯一的区别是tokanization *

using ProjName.AuthApiSoap;  // USE AUth Webserice Reference

   public Tuple<string, string, string> CreateTransaction(long profile_id, long payment_profile_id, decimal amt, string DDD)
        
            CustomerProfileWS.ProfileTransAuthCaptureType auth_capture = new CustomerProfileWS.ProfileTransAuthCaptureType();
            auth_capture.customerProfileId = profile_id;
            auth_capture.customerPaymentProfileId = payment_profile_id;
            auth_capture.amount = amt;//1.00m;
            auth_capture.order = new CustomerProfileWS.OrderExType();
            POSLib.POSManager objManager = new POSLib.POSManager();
            auth_capture.order.invoiceNumber = objManager.GetTimestamp(DateTime.Now);
            DateTime now = DateTime.Now;
            auth_capture.order.description = "Service  " + DDD;
            CustomerProfileWS.ProfileTransactionType trans = new CustomerProfileWS.ProfileTransactionType();
            trans.Item = auth_capture;
            CustomerProfileWS.CreateCustomerProfileTransactionResponseType response = SoapAPIUtilities.Service.CreateCustomerProfileTransaction(SoapAPIUtilities.MerchantAuthentication, trans, null);

            string AuthTranMsg = "";
            string AuthTranCode = "";
            for (int i = 0; i < response.messages.Length; i++)
            
                AuthTranMsg = response.messages[i].text;  // To Get Message n for loop to check the [i] is not empty 
            
            for (int i = 0; i < response.messages.Length; i++)
            
                AuthTranCode = response.messages[i].code;   // To Get Code n for loop to check the [i] is not empty 
            
            var tCompResp = new Tuple<string, string, string>(AuthTranCode, AuthTranMsg, response.directResponse);
            return tCompResp;
        

这是拆分响应消息的方法(格式和顺序将为所有交易/响应的网络服务上的固定)

 var tResp = objManager.CreateTransaction(profID, paymProfID, Convert.ToDecimal(PmtToday), DDD);
                    string respCCNo = "";
                    string RespCCType = "";
                    string InvoiceNo = "";
                    string transType = "";
                    string approvalCode = "";
                    string AmtRequested = "";
                    string respName = "";
                    string respReasonText = "";
                    string respMD5Hash = "";
                    string respEmailId = "";
                    string respReasonCode = "";
                    string respMethod = "";
                    string respAVSResultCode = "";
                    string responseCode = "";
                    string transactionId = "0";
                    if (!string.IsNullOrEmpty(tCompResp.Item3))
                    
                        string[] arrRespParts = tCompResp.Item3.Replace("|", "").Split(',');
                        responseCode = arrRespParts[0];
                        respReasonCode = arrRespParts[2];
                        respReasonText = arrRespParts[3];
                        approvalCode = arrRespParts[4];
                        respAVSResultCode = arrRespParts[5];
                        transactionId = arrRespParts[6].Replace("|", "");
                        InvoiceNo = arrRespParts[7];
                        AmtRequested = arrRespParts[9];
                        transType = arrRespParts[10];
                        respMethod = arrRespParts[11];
                        respName = arrRespParts[13] + " " + arrRespParts[14];
                        respEmailId = arrRespParts[23];
                        respMD5Hash = arrRespParts[37];
                        respCCNo = arrRespParts[50];
                        RespCCType = arrRespParts[51];
                    

===================================AIM 代码

 public Tuple<string, string, string> ECheckCreateTransAIM(string amount, string bankRoutingNo, string bankAccNo, string bankAccType, string bankName, string bankAccName, string echeckType, bool isCustomerEmail, string customerEmail, string mechantEMail)
        
            //CustomValidator1.ErrorMessage = "";
            string AuthNetVersion = "3.1"; // Contains CCV support

            WebClient webClientRequest = new WebClient();
            System.Collections.Specialized.NameValueCollection InputObject = new System.Collections.Specialized.NameValueCollection(30);
            System.Collections.Specialized.NameValueCollection ReturnObject = new System.Collections.Specialized.NameValueCollection(30);
            byte[] ReturnBytes;
            string[] ReturnValues;
            string ErrorString;
            InputObject.Add("x_version", AuthNetVersion);
            InputObject.Add("x_delim_data", "True");
            InputObject.Add("x_login", MERCHANT_NAME);
            InputObject.Add("x_tran_key", TRANSACTION_KEY);
            InputObject.Add("x_relay_response", "False");
            //----------------------Set to False to go Live--------------------
            InputObject.Add("x_test_request", "False");
            //---------------------------------------------------------------------
            InputObject.Add("x_delim_char", ",");
            InputObject.Add("x_encap_char", "|");
            if (isCustomerEmail)
            
                InputObject.Add("x_email", customerEmail);
                InputObject.Add("x_email_customer", "TRUE");                     //Emails Customer
            
            InputObject.Add("x_merchant_email", mechantEMail);
            // FOR echeck            
            InputObject.Add("x_bank_aba_code", bankRoutingNo);
            InputObject.Add("x_bank_acct_num", bankAccNo);
            InputObject.Add("x_bank_acct_type", bankAccType);
            InputObject.Add("x_bank_name", bankName);
            InputObject.Add("x_bank_acct_name", bankAccName);
            InputObject.Add("x_method", "ECHECK");
            InputObject.Add("x_type", "AUTH_CAPTURE");
            InputObject.Add("x_amount", string.Format("0:c2", Convert.ToDouble(amount)));
            // Currency setting. Check the guide for other supported currencies           
            //needto change it to Actual Server URL
            //Set above Testmode=off to go live
            webClientRequest.BaseAddress = eCheckBaseAddress;  //"https://apitest.authorize.net/soap/v1/Service.asmx"; //"https://secure.authorize.net/gateway/transact.dll";
            ReturnBytes = webClientRequest.UploadValues(webClientRequest.BaseAddress, "POST", InputObject);
            ReturnValues = System.Text.Encoding.ASCII.GetString(ReturnBytes).Split(",".ToCharArray());
            if (ReturnValues[0].Trim(char.Parse("|")) == "1")  // Succesful Transaction
            
                //AuthNetCodeLabel.Text = ReturnValues[4].Trim(char.Parse("|")); // Returned Authorisation Code
                //AuthNetTransIDLabel.Text = ReturnValues[6].Trim(char.Parse("|")); // Returned Transaction ID
                var tCompResp = new Tuple<string, string, string>("I00001", ReturnValues[3].Trim(char.Parse("|")), string.Join(",", ReturnValues));
                return tCompResp;
            
            else
            
                // Error!
                ErrorString = ReturnValues[3].Trim(char.Parse("|")) + " (" + ReturnValues[2].Trim(char.Parse("|")) + ")";
                if (ReturnValues[2].Trim(char.Parse("|")) == "45")
                
                    if (ErrorString.Length > 1)
                        ErrorString += "<br />n";

                    // AVS transaction decline
                    ErrorString += "Address Verification System (AVS) " +
                      "returned the following error: ";

                    switch (ReturnValues[5].Trim(char.Parse("|")))
                    
                        case "A":
                            ErrorString += " the zip code entered does not match the billing address.";
                            break;
                        case "B":
                            ErrorString += " no information was provided for the AVS check.";
                            break;
                        case "E":
                            ErrorString += " a general error occurred in the AVS system.";
                            break;
                        case "G":
                            ErrorString += " the credit card was issued by a non-US bank.";
                            break;
                        case "N":
                            ErrorString += " neither the entered street address nor zip code matches the billing address.";
                            break;
                        case "P":
                            ErrorString += " AVS is not applicable for this transaction.";
                            break;
                        case "R":
                            ErrorString += " please retry the transaction; the AVS system was unavailable or timed out.";
                            break;
                        case "S":
                            ErrorString += " the AVS service is not supported by your credit card issuer.";
                            break;
                        case "U":
                            ErrorString += " address information is unavailable for the credit card.";
                            break;
                        case "W":
                            ErrorString += " the 9 digit zip code matches, but the street address does not.";
                            break;
                        case "Z":
                            ErrorString += " the zip code matches, but the address does not.";
                            break;
                    
                

            
            var tCompRespFail = new Tuple<string, string, string>(ReturnValues[6].ToString(), ErrorString, string.Join(",", ReturnValues));
            return tCompRespFail;


        

CIM 代码(Tokanisation(无卡方法)

   public Tuple<string, string, string> CreateTransaction(long profile_id, long payment_profile_id, decimal amt, string DDD)
        
            CustomerProfileWS.ProfileTransAuthCaptureType auth_capture = new CustomerProfileWS.ProfileTransAuthCaptureType();
            auth_capture.customerProfileId = profile_id;
            auth_capture.customerPaymentProfileId = payment_profile_id;
            auth_capture.amount = amt;//1.00m;
            auth_capture.order = new CustomerProfileWS.OrderExType();
            POSLib.POSManager objManager = new POSLib.POSManager();
            auth_capture.order.invoiceNumber = objManager.GetTimestamp(DateTime.Now);
            DateTime now = DateTime.Now;
            auth_capture.order.description = "Service  " + DDD;
            CustomerProfileWS.ProfileTransactionType trans = new CustomerProfileWS.ProfileTransactionType();
            trans.Item = auth_capture;
            CustomerProfileWS.CreateCustomerProfileTransactionResponseType response = SoapAPIUtilities.Service.CreateCustomerProfileTransaction(SoapAPIUtilities.MerchantAuthentication, trans, null);

            string AuthTranMsg = "";
            string AuthTranCode = "";
            for (int i = 0; i < response.messages.Length; i++)
            
                AuthTranMsg = response.messages[i].text;  // To Get Message n for loop to check the [i] is not empty 
            
            for (int i = 0; i < response.messages.Length; i++)
            
                AuthTranCode = response.messages[i].code;   // To Get Code n for loop to check the [i] is not empty 
            
            var tCompResp = new Tuple<string, string, string>(AuthTranCode, AuthTranMsg, response.directResponse);
            return tCompResp;
        

【讨论】:

以上是关于在.NET 6.0中自定义接口路由的主要内容,如果未能解决你的问题,请参考以下文章

.Net 6.0全局异常捕获

在.NET 6.0中使用不同的托管模型

在.NET 6.0中使用不同的托管模型

asp.ner core 6.0路由找不到

如何在 Swift 中自定义 UISlider 值

在.net桌面程序中自定义鼠标光标