Web APi之控制器选择Action方法过程(九)

简介:

前言

前面我们叙述了关于控制器创建的详细过程,在前面完成了对控制器的激活之后,就是根据控制器信息来查找匹配的Action方法,这就是本节要讲的内容。当请求过来时首先经过宿主处理管道然后进入Web API消息处理管道,接着就是控制器的创建和执行控制器即选择匹配的Action方法最终并作出响应(在Action方法上还涉及到模型绑定、过滤器等,后续讲)。从上知,这一系列大部分内容都已囊括,我们这一系列也就算快完事,在后面将根据这些管道事件以及相关的处理给出相应的练习来熟练掌握,希望此系列能帮助到想学习原理之人。

HttpControllerContext

我们回顾下此控制器上下文创建的过程:

1
2
3
4
5
HttpControllerContext = controllerContext =  new  HttpControllerContext(descriptor.Configuration,routeData,request)
{
    Controller = controller;
    ControllerDescrptor = descriptor
}

从上知以及从其名称可得知,控制器上下文就是将创建的控制器以及其描述信息封装到其中,方便调用而已。接下来就进入控制器上Action方法的选择,请往下看!

ApiController

相信大家对此类并不敏感,只要你创建了Web API控制器都是继承于该类,下面我们来看看此类的定义:

复制代码
 1 public abstract class ApiController : IHttpController, IDisposable
 2 {
 3     // Fields
 4     private HttpConfiguration _configuration;
 5     private HttpControllerContext _controllerContext;
 6     private bool _disposed;
 7     private ModelStateDictionary _modelState;
 8     private HttpRequestMessage _request;
 9     private UrlHelper _urlHelper;
10 
11     // Methods
12     protected ApiController();
13     public void Dispose();
14     protected virtual void Dispose(bool disposing);
15     public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken);
16 }
复制代码

此上知该类为抽象类型,并在其中定义了一个ExecuteAsync()方法,此方法是此类中最重要的一个方法,因为控制器的执行过程就是在此方法中进行,下面我们继续来看看此方法的定义及实现。

复制代码
 1 public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
 2 {
 3     if (this._request != null)
 4     {
 5         throw Error.InvalidOperation(SRResources.CannotSupportSingletonInstance, new object[] { typeof(ApiController).Name, typeof(IHttpControllerActivator).Name });
 6     }
 7     this.Initialize(controllerContext);
 8     if (this._request != null)
 9     {
10         this._request.RegisterForDispose(this);
11     }
12     HttpControllerDescriptor controllerDescriptor = controllerContext.ControllerDescriptor;
13     ServicesContainer controllerServices = controllerDescriptor.Configuration.Services;
14     HttpActionDescriptor actionDescriptor = controllerServices.GetActionSelector().SelectAction(controllerContext);
15     HttpActionContext actionContext = new HttpActionContext(controllerContext, actionDescriptor);
16 }
复制代码

上述我只列出此节要讲的内容,有关过滤器以及其管道和授权等等都已略去,后续会讲。最重要的就是上述红色标记的四个了,下面我一一来查看:

第一步:HttpControllerDescriptor controllerDescriptor = controllerContenxt.ControllerDescriptor

在创建并激活控制器后其控制器上下文也同时生成,我们从控制器上下文中获得有关控制器的描述即HttpControllerDescriptor 

第二步:ServicesContainer controllerServices = controllerDescriptor.Configuration.Services;

从控制器的描述中的属性Configuration即HttpConfiguraion来获得其有关控制器的服务容器即Services

由于第三步涉及到其他类,此时我们将另起一段来叙述,请继续往下看!

HttpActionDescriptor

第三步:HttpActionDescriptor actionDescriptor = controllerServices.GetActionSelector().........

这一步就是重点所在,我们在前面叙述了多次关于服务容器ServicesContainer,这里不过是获得有关具体的服务容器而已,我们看看GetActionSelector方法:

1
2
3
4
public  static  IHttpActionSelector GetActionSelector( this  ServicesContainer services)
{
     return  services.GetServiceOrThrow<IHttpActionSelector>();
}

到了这里看过前面的文章相信大家不会陌生,就是从服务器容器中去获得我们注入的具体的实现。我们继续看看其子类DefaultServices具体实现到底是什么  

1
this .SetSingle<IHttpActionSelector>( new  ApiControllerActionSelector());

ApiControllerActionSelector

从上得知,注入的服务为ApiControllerActionSelector类,我们继续追查此类:

复制代码
 1 public class ApiControllerActionSelector : IHttpActionSelector
 2 {
 3     // Fields
 4     private readonly object _cacheKey;
 5     private ActionSelectorCacheItem _fastCache;
 6     private const string ActionRouteKey = "action";
 7     private const string ControllerRouteKey = "controller";
 8 
 9     // Methods
10     public ApiControllerActionSelector();
11     public virtual ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
12     private ActionSelectorCacheItem GetInternalSelector(HttpControllerDescriptor controllerDescriptor);
13     public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
14 
15     // Nested Types
16     private class ActionSelectorCacheItem
17     {
18         // Fields
19         private readonly ReflectedHttpActionDescriptor[] _actionDescriptors;
20         private readonly ILookup<string, ReflectedHttpActionDescriptor> _actionNameMapping;
21         private readonly IDictionary<ReflectedHttpActionDescriptor, string[]> _actionParameterNames;
22         private readonly HttpMethod[] _cacheListVerbKinds;
23         private readonly ReflectedHttpActionDescriptor[][] _cacheListVerbs;
24         private readonly HttpControllerDescriptor _controllerDescriptor;
25 
26         // Methods
27         public ActionSelectorCacheItem(HttpControllerDescriptor controllerDescriptor);
28         private static string CreateAmbiguousMatchList(IEnumerable<HttpActionDescriptor> ambiguousDescriptors);
29         private ReflectedHttpActionDescriptor[] FindActionsForVerb(HttpMethod verb);
30         private ReflectedHttpActionDescriptor[] FindActionsForVerbWorker(HttpMethod verb);
31         private IEnumerable<ReflectedHttpActionDescriptor> FindActionUsingRouteAndQueryParameters(HttpControllerContext controllerContext, IEnumerable<ReflectedHttpActionDescriptor> actionsFound, bool hasActionRouteKey);
32         public ILookup<string, HttpActionDescriptor> GetActionMapping();
33         private static bool IsSubset(string[] actionParameters, HashSet<string> routeAndQueryParameters);
34         private static bool IsValidActionMethod(MethodInfo methodInfo);
35         private static List<ReflectedHttpActionDescriptor> RunSelectionFilters(HttpControllerContext controllerContext, IEnumerable<HttpActionDescriptor> descriptorsFound);
36         public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
37 
38         // Properties
39         public HttpControllerDescriptor HttpControllerDescriptor { get; }
40     }
41 
42     private class LookupAdapter : ILookup<string, HttpActionDescriptor>, IEnumerable<IGrouping<string, HttpActionDescriptor>>, IEnumerable
43     {
44         // Fields
45         public ILookup<string, ReflectedHttpActionDescriptor> Source;
46 
47         // Methods
48         public LookupAdapter();
49         public bool Contains(string key);
50         public IEnumerator<IGrouping<string, HttpActionDescriptor>> GetEnumerator();
51         IEnumerator IEnumerable.GetEnumerator();
52 
53         // Properties
54         public int Count { get; }
55         public IEnumerable<HttpActionDescriptor> this[string key] { get; }
56     }
57 }
复制代码

在此类中还额外包括两个私有的类: ActionSelectorCacheItem 和 LookupAdaper 。先保留这两个类,我们首先看看SelectAtion()方法

1
2
3
4
5
6
7
8
public  virtual  HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
     if  (controllerContext ==  null )
     {
         throw  Error.ArgumentNull( "controllerContext" );
     }
     return  this .GetInternalSelector(controllerContext.ControllerDescriptor).SelectAction(controllerContext);
}

我们从此方法得知,此方法的实现本质是调用了返回值为  ActionSelectorCacheItem中的方法GetInternalSelector,我们从ActionSelectorCacheItem来猜此方法应该是生成一个控制器方法的缓存对象,所以此ActionSelectorCacheItem类型将是我们接下来介绍的重中之重。它被用来筛选Action()方法我们看看这个类:

复制代码
 1 private class ActionSelectorCacheItem
 2 {
 3     // Fields
 4     private readonly ReflectedHttpActionDescriptor[] _actionDescriptors;
 5     private readonly ILookup<string, ReflectedHttpActionDescriptor> _actionNameMapping;
 6     private readonly IDictionary<ReflectedHttpActionDescriptor, string[]> _actionParameterNames;
 7     private readonly HttpMethod[] _cacheListVerbKinds;
 8     private readonly ReflectedHttpActionDescriptor[][] _cacheListVerbs;
 9     private readonly HttpControllerDescriptor _controllerDescriptor;
10 
11     // Methods
12     public ActionSelectorCacheItem(HttpControllerDescriptor controllerDescriptor);
13     public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
14 
15     // Properties
16     public HttpControllerDescriptor HttpControllerDescriptor { get; }
17 }
复制代码

以上四个属性并返回为相应的返回值是筛选方法的五个步骤之一,还有一个当然是构造函数的初始化了,这一切都将在构造函数中进行筛选,查看其构造函数:

复制代码
public ActionSelectorCacheItem(HttpControllerDescriptor controllerDescriptor)
{
    this._actionParameterNames = new Dictionary<ReflectedHttpActionDescriptor, string[]>();
    this._cacheListVerbKinds = new HttpMethod[] { HttpMethod.Get, HttpMethod.Put, HttpMethod.Post };
    this._controllerDescriptor = controllerDescriptor;
    MethodInfo[] infoArray2 = Array.FindAll<MethodInfo>(this._controllerDescriptor.ControllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance), new Predicate<MethodInfo>(ApiControllerActionSelector.ActionSelectorCacheItem.IsValidActionMethod));
    this._actionDescriptors = new ReflectedHttpActionDescriptor[infoArray2.Length];
    for (int i = 0; i < infoArray2.Length; i++)
    {
        MethodInfo methodInfo = infoArray2[i];
        ReflectedHttpActionDescriptor key = new ReflectedHttpActionDescriptor(this._controllerDescriptor, methodInfo);
        this._actionDescriptors[i] = key;
        HttpActionBinding actionBinding = key.ActionBinding;
        this._actionParameterNames.Add(key, (from binding in actionBinding.ParameterBindings
            where (!binding.Descriptor.IsOptional && TypeHelper.IsSimpleUnderlyingType(binding.Descriptor.ParameterType)) && binding.WillReadUri()
            select binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray<string>());
    }
    this._actionNameMapping = this._actionDescriptors.ToLookup<ReflectedHttpActionDescriptor, string>(actionDesc => actionDesc.ActionName, StringComparer.OrdinalIgnoreCase);
    int length = this._cacheListVerbKinds.Length;
    this._cacheListVerbs = new ReflectedHttpActionDescriptor[length][];
    for (int j = 0; j < length; j++)
    {
        this._cacheListVerbs[j] = this.FindActionsForVerbWorker(this._cacheListVerbKinds[j]);
    }
}
复制代码

上述用五种颜色来标记五种筛选,我们由上至下一一进行简短解释:

  • 初始化筛选

通过传递进来的HttpControllerDesccriptor对象而给其该类此对象的变量属性,然后根据反射条件 BindingFlags.Public | BindingFlags.Instance 来获得控制器中的所有方法,最终保存在MethodInfo[]数组中。

  • ReflectedHttpActionDescriptor[] _actiondescriptors初始化

ReflectedHttpActionDescriptor是一个抽象类,通过上述已经控制器中的方法封装到MethodInfo[]数组中,此时将循环此数组并将每一个MehodInfo实例封装成ReflectedHttpActionDescriptor对象,并将每个实例封装到此对象数组中,通过该类名称,顾名思义也知,通过反射来获取Action方法的元数据信息。

  •  IDictionary<ReflectedHttpActionDesciptor,string[]> _actionparameterNames初始化

利用获得的ReflectedHttpActionDescriptor进行分析,并将其放到字典中,将此类作为键,而该方法的参数名称。

  • ILookup<string,ReflectedHttpActionDescriptor> _actionNameMapping初始化 

还是利用获得ReflectedHttpActionDescriptor来依据Action()方法名称来进行分组。

  • ReflectedHttpActionDescriptor[][] _cacheListVerbs初始化

利用获得的ReflectedHttpActionDescriptor进行依据Http方法类型来进行分类。

上面的五个只是初始化赋值,是为了下面筛选Action名称做准备,似乎有点难以理解,请看下面我们一一进行讲解!

Action名称筛选 

既然是筛选名称则要用到 ActionSelectorCacheItem 中的 SelectAction() 方法了,我们来仔细看看此方法的定义及实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public  HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
     string  str;
     ReflectedHttpActionDescriptor[] descriptorArray;
     Func<ReflectedHttpActionDescriptor,  bool > predicate =  null ;
     bool  hasActionRouteKey = controllerContext.RouteData.Values.TryGetValue< string >( "action" out  str);
     HttpMethod incomingMethod = controllerContext.Request.Method;
     if  (hasActionRouteKey)
     {
         ReflectedHttpActionDescriptor[] source =  this ._actionNameMapping[str].ToArray<ReflectedHttpActionDescriptor>();
         if  (source.Length == 0)
         {
             throw  new  HttpResponseException(controllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, Error.Format(SRResources.ResourceNotFound,  new  object [] { controllerContext.Request.RequestUri }), Error.Format(SRResources.ApiControllerActionSelector_ActionNameNotFound,  new  object [] {  this ._controllerDescriptor.ControllerName, str })));
         }
         if  (predicate ==  null )
         {
             predicate = actionDescriptor => actionDescriptor.SupportedHttpMethods.Contains(incomingMethod);
         }
         descriptorArray = source.Where<ReflectedHttpActionDescriptor>(predicate).ToArray<ReflectedHttpActionDescriptor>();
     }
     else
     {
         descriptorArray =  this .FindActionsForVerb(incomingMethod);
     }
     if  (descriptorArray.Length == 0)
     {
         throw  new  HttpResponseException(controllerContext.Request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed, Error.Format(SRResources.ApiControllerActionSelector_HttpMethodNotSupported,  new  object [] { incomingMethod })));
     }
     IEnumerable<ReflectedHttpActionDescriptor> descriptorsFound =  this .FindActionUsingRouteAndQueryParameters(controllerContext, descriptorArray, hasActionRouteKey);
     List<ReflectedHttpActionDescriptor> list = RunSelectionFilters(controllerContext, descriptorsFound);
     descriptorArray =  null ;
     descriptorsFound =  null ;
     switch  (list.Count)
     {
         case  0:
             throw  new  HttpResponseException(controllerContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, Error.Format(SRResources.ResourceNotFound,  new  object [] { controllerContext.Request.RequestUri }), Error.Format(SRResources.ApiControllerActionSelector_ActionNotFound,  new  object [] {  this ._controllerDescriptor.ControllerName })));
 
         case  1:
             return  list[0];
     }
     string  str2 = CreateAmbiguousMatchList((IEnumerable<HttpActionDescriptor>) list);
     throw  Error.InvalidOperation(SRResources.ApiControllerActionSelector_AmbiguousMatch,  new  object [] { str2 });
}

我们通过前面的行号来讲解,我们知道在Web API的配置文件中默认是没有Action方法,当然你也手动配置Action方法,所以此时就会出现两种情况,由上亦知:

(6)我们从控制器上下文中获取路由对象RouteData,并且在其Values属性中查找action对应的值并返回该值

  • 若注册路由中有Action名称  

当查找到有Action名称时,看(10)根据上述初始化的_actionNameMapping来获得相应的action方法的元数据信息并返回一个ReflectedHttpActionDescriptor数组也就是source。再看(17)我们从ReflectedHttpActionDescriptor的属性集合SupportedHttpMethods即Http支持的方法中去找到根据从(7)控制器上下文中获得请求过来的Http方法,最后以(17)为条件获取到满足条件的元数据信息ReflectedHttpActionDescriptor数组即descriptorArray中。

关于此注册路由有Action名称的例子就不再叙述,与在MVC框架中请求Action方法类似。

  • 若注册路由无Action名称,则依据Http方法来决定Action方法名称

此时则利用上述初始化依据Http方法类型分类的_cacheListVerbs根据控制器上下文请求过来的方法类型来进行筛选决定其Http方法 ,我们举例来说明:

我们配置其路由为:  

1
2
3
4
5
config.Routes.MapHttpRoute(
               name:  "DefaultApi" ,
               routeTemplate:  "api/{controller}/{id}" ,
               defaults:  new  { id = RouteParameter.Optional }
           );

我们的Action方法为:

1
2
3
4
5
6
7
8
9
public  void  GetProduct( int  id) { }
 
public  void  PostProduct( int  id) { }
 
public  void  PutProduct( int  id) { }
 
public  void  DeleteProduct( int  id) { }
 
public  void  HeadProduct( int  id) { }

当我们进行如下Get请求时:

1
2
3
4
5
6
7
8
9
10
$.ajax({
     type:  "get" ,
     url:  "http://localhost:7114/api/product/1" ,
     dataType:  "json" ,
     contentType:  "application/json; charset=utf-8" ,    
     cache:  false ,
     success: function (r) {
         console.log(r);
     }
});

此时会匹配到对应的如下Action方法:

其他的方法请求也类似Get除了Post之外,下面我们进行Post请求:  

观察此结果似乎也没什么不一样,No,我们在Action方法里再添加一个方法如下:

1
public  void  Other( int  id) { }

接下来我们再来进行Post请求,你会接收到如下响应错误

{"Message":"An error has occurred.","ExceptionMessage":"Multiple actions were found that match the request: \r\nVoid PostProduct(Int32) on type MvcWebApi.Controllers.ProductController\r\nVoid Other(Int32) on type MvcWebApi.Controllers.ProductController".......}

出错原因是有多个Action方法被匹配到,那么为什么会出现这样的错误呢?这就要说到约定了:

定义在控制器上的Action方法默认是仅支持一种Http方法,并且这个支持的Http方法决定于Action方法的名称,明确来讲,具有以上Action方法的前缀与对应的Http方法将默认支持,比如以上的GetProduct,那么默认只支持Get。但是如果Action名称没有这样的前缀,如以上的Other方法,那么默认支持Post。

基于以上默认约定,所以此时PostProduct和Other都会被匹配到,但是Action默认又仅支持一种Http方法,所以此时会出现如上错误。

这种有Action方法决定它所支持的Http方法的策略与RESET架构风格完全吻合,因为后者推荐直接采用Http方法来表示针对目标资源的操作方式,题外话,但是很多情况下,我们需要一个Action方法既支持Get又支持Post方法这个时候就需要用到ActionHttpMethodProvider特性解决。  

依据请求参数名称及个数筛选 

  • 有参数

在这种情况下,会先把路由数据对象的Values属性中的Keys值存放在一个集合A中,然后再获取当前请求的查询字符串集合,并且把集合中的所有Key值添加到集合A中,这就使的所有请求的参数名称都在一个集合中,然后就会从上述初始化的第二个的结果中根据当前的ReflectedHttpActionDescriptor类型实例(这里是接着上述初始化第三个的流程,所以这里是ReflectedHttpActionDescriptor类型的数组遍历执行)从_actionParameterNames获取对应的参数名称数组,然后是集合A会和获取的参数名称数组做一一的比较,看看集合A中是否包含参数名称,如果都有了则是满足条件。

这里返回的依然可能是ReflectedHttpActionDescriptor类型的数组,因为在一个方法有重载时,比如说Get(stringa)和Get(string a,string b)两个方法时,请求中如果有a和b两个参数的话,Get(string a)也是满足条件的。

  • 无参数

分别是利用 IActionMethodSelector、IActionMethodSelector以及IActionMethodSelector 来实现,就不再叙述,有兴趣的朋友可以查看其源码。

总结

关于控制器的执行选择Action方法用其详细示意图来表示,如下:来源【控制器执行过程】

未完待续:接下来将叙述过滤器,敬请期待。。。。。。。











本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/4846944.html,如需转载请自行联系原作者

目录
相关文章
|
22天前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
|
5月前
|
JSON 数据可视化 API
Python 中调用 DeepSeek-R1 API的方法介绍,图文教程
本教程详细介绍了如何使用 Python 调用 DeepSeek 的 R1 大模型 API,适合编程新手。首先登录 DeepSeek 控制台获取 API Key,安装 Python 和 requests 库后,编写基础调用代码并运行。文末包含常见问题解答和更简单的可视化调用方法,建议收藏备用。 原文链接:[如何使用 Python 调用 DeepSeek-R1 API?](https://apifox.com/apiskills/how-to-call-the-deepseek-r1-api-using-python/)
|
3月前
|
移动开发 前端开发 JavaScript
H5 页面与 Web 页面的制作方法
H5页面制作利用HTML5、CSS3和JavaScript技术,结合H5编辑器或框架(如Adobe Dreamweaver、Ionic),注重移动设备兼容性与响应式布局。Web页面制作则基于传统HTML、CSS和JavaScript,借助文本编辑器或IDE完成开发。两者区别在于技术版本、交互性和浏览器支持:H5更互动、现代,但可能不兼容旧浏览器;Web页面更静态、兼容性广。根据需求选择:高交互选H5,广泛兼容选Web。
173 6
|
3月前
|
人工智能 搜索推荐 IDE
突破网页数据集获取难题:Web Unlocker API 助力 AI 训练与微调数据集全方位解决方案
本文介绍了Web Unlocker API、Web-Scraper和SERP API三大工具,助力解决AI训练与微调数据集获取难题。Web Unlocker API通过智能代理和CAPTCHA绕过技术,高效解锁高防护网站数据;Web-Scraper支持动态内容加载,精准抓取复杂网页信息;SERP API专注搜索引擎结果页数据抓取,适用于SEO分析与市场研究。这些工具大幅降低数据获取成本,提供合规保障,特别适合中小企业使用。粉丝专属体验入口提供2刀额度,助您轻松上手!
126 2
|
3月前
|
人工智能 运维 安全
网络安全公司推荐:F5荣膺IDC全球Web应用与API防护领导者
网络安全公司推荐:F5荣膺IDC全球Web应用与API防护领导者
77 4
|
4月前
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
6月前
|
人工智能 前端开发 API
Gemini Coder:基于 Google Gemini API 的开源 Web 应用生成工具,支持实时编辑和预览
Gemini Coder 是一款基于 Google Gemini API 的 AI 应用生成工具,支持通过文本描述快速生成代码,并提供实时代码编辑和预览功能,简化开发流程。
315 38
Gemini Coder:基于 Google Gemini API 的开源 Web 应用生成工具,支持实时编辑和预览
|
4月前
|
机器学习/深度学习 开发框架 API
Python 高级编程与实战:深入理解 Web 开发与 API 设计
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧以及数据科学和机器学习。本文将深入探讨 Python 在 Web 开发和 API 设计中的应用,并通过实战项目帮助你掌握这些技术。
|
5月前
|
存储 JSON JavaScript
WEB CAD插件通过上下文对象MxPluginContext修改UI界面的方法
本文介绍了如何使用MxPluginContext动态控制MxCAD项目的UI界面。通过该上下文对象,开发者可以灵活设置UI配置,如控制操作栏显隐、编辑按钮、添加侧边栏等。具体方法包括调用`getUiConfig()`获取并修改`mxUiConfig.json`中的属性,实现界面的定制化。此外,还提供了控制命令行聚焦的功能,解决输入框焦点锁定问题。详细代码示例和效果对比图展示了具体实现步骤,帮助开发者更好地适配项目需求。
|
7月前
|
Kubernetes 安全 Devops
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
205 10
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙

热门文章

最新文章