当前位置:  开发笔记 > 编程语言 > 正文

如何在jQuery Ajax调用之后管理重定向请求

如何解决《如何在jQueryAjax调用之后管理重定向请求》经验,为你挑选了26个好方法。

我正在使用$.post()Ajax调用servlet,然后使用生成的HTML片段替换div用户当前页面中的元素.但是,如果会话超时,服务器会发送重定向指令以将用户发送到登录页面.在这种情况下,jQuery正在用div登录页面的内容替换元素,迫使用户的目光确实见证了罕见的场景.

如何使用jQuery 1.2.6从Ajax调用管理重定向指令?



1> Steg..:

我阅读了这个问题并实现了关于将响应状态代码设置为278的方法,以避免浏览器透明地处理重定向.尽管这很有效,但我有点不满意,因为它有点像黑客.

经过更多的挖掘,我抛弃了这种方法并使用了JSON.在这种情况下,对ajax请求的所有响应都具有状态代码200,并且响应主体包含在服务器上构造的JSON对象.然后,客户端上的javascript可以使用JSON对象来决定它需要做什么.

我遇到了类似的问题.我执行一个有两个可能响应的ajax请求:一个将浏览器重定向到一个新页面,另一个用新的替换当前页面上的现有HTML表单.执行此操作的jquery代码如下所示:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        } else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

JSON对象"data"在服务器上构造为具有2个成员:data.redirect和data.form.我发现这种方法要好得多.


正如http://stackoverflow.com/questions/503093/how-can-i-make-a-redirect-page-in-jquery中的解决方案中所述,最好使用window.location.replace(data.redirect) ; 而不是window.location.href = data.redirect;
仅当您对服务器有控制权时,此方法才有效
@Sergei Golos的原因是,如果你进行HTTP重定向,重定向实际上永远不会到达ajax成功回调.浏览器处理重定向,提供包含重定向目标内容的200代码.
为什么在动作上使用HTTP代码不是更好的原因.例如,一个307代码是HTTP临时重定向?
如果它已经显示如何在服务器上完成此答案将更有帮助.
服务器代码会是什么样的?具有data.form和data.redirect的返回值.基本上我如何确定我是否重定向?

2> 小智..:

我解决了这个问题:

    向响应添加自定义标头:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    

    将JavaScript函数绑定到ajaxSuccess事件并检查标头是否存在:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    


我喜欢标题方法,但也想 - 比如@ mwoods79 - 重定向到哪里的知识不应该重复.我通过添加标题REDIRECT_LOCATION而不是布尔值来解决这个问题.
多么棒的解决方案.我喜欢一站式解决方案的想法.我需要检查403状态,但我可以在身体上使用ajaxSuccess绑定(这是我真正想要的.)谢谢.
我刚刚做了这个,发现我需要ajaxComplete,我在使用$ .get()函数,除了200之外的任何状态都没有触发.事实上,我可能只是绑定到ajaxError.有关详细信息,请参阅下面的答案.
注意在重定向后设置响应的标题.如本页其他答案中所述,重定向可能对ajaxSucces处理程序是透明的.因此,我在登录页面的GET响应中包含了标题(最终只在我的场景中触发了ajaxSuccess).
在许多情况下都可以,但是如果您的框架处理授权会怎么样?

3> Thomas Hanse..:

没有浏览器正确处理301和302响应.事实上,该标准甚至表示他们应该"透明地"处理它们,这对于Ajax Library供应商来说是一个非常令人头痛的问题.在Ra-Ajax中,我们被迫使用HTTP响应状态代码278(只是一些"未使用的"成功代码)来处理来自服务器的透明重定向...

这真让我烦恼,如果有人在W3C中有一些"拉",我会很高兴你能让W3C 知道我们真的需要自己处理301和302代码......!;)


我认为278应该成为官方HTTP规范的一部分.
它是否已经透明地处理它们?如果资源已移动,则透明地处理它意味着在提供的URL上重复请求.这就是我期望使用XMLHttpRequest API.

4> Elliot Varga..:

最终实现的解决方案是使用包装器来执行Ajax调用的回调函数,并在此包装器中检查返回的HTML块上是否存在特定元素.如果找到该元素,则包装器执行重定向.如果没有,则包装器将调用转发给实际的回调函数.

例如,我们的包装函数类似于:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

然后,在进行Ajax调用时,我们使用了类似的东西:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

这对我们有用,因为所有Ajax调用总是在我们用来替换页面的DIV元素中返回HTML.此外,我们只需要重定向到登录页面.


请注意,这可以缩短为函数cbWrapper(funct){return function(data){if($("#myForm",data).size()> 0)top.location.href ="login"; 其他功能(数据); }.然后在调用.post时只需要cbWrapper(myActualCB).是的,评论中的代码是一团糟但应该注意:)

5> BrianY..:

我喜欢Timmerz的方法,略带柠檬味.如果你有机会返回的contentType的text/html,当你期待JSON,你是最有可能被重定向.在我的情况下,我只是重新加载页面,它被重定向到登录页面.哦,并检查jqXHR状态是200,这看起来很傻,因为你在错误函数中,对吧?否则,合法的错误情况将强制迭代重新加载(oops)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});



6> Till..:

使用低级别$.ajax()电话:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

试试这个重定向:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}


好家伙!对不起,我不得不"取消"你的答案,它仍然非常有帮助.事实上,重定向由XMLHttpRequest自动管理,因此我总是在重定向(叹息!)后获得200状态代码.我想我必须做一些讨厌的事情,比如解析HTML并找一个标记.
注意:这不适用于重定向.ajax将转到新页面并返回其状态代码.
顺便说一句,$ .ajax()不是非常非常低级.它只是jQuery方面的低级别,因为有$ .get,$ .post等比$ .ajax及其所有选项简单得多.

7> Juri..:

我只是想分享我的方法,因为这可能会对某人有所帮助:

我基本上包括一个JavaScript模块,它处理身份验证的东西,如显示用户名,以及处理重定向到登录页面的情况.

我的场景:我们之间基本上有一个ISA服务器,它监听所有请求并使用302和位置标头响应我们的登录页面.

在我的JavaScript模块中,我的初始方法是类似的

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

问题(正如许多人已经提到的那样)是浏览器自己处理重定向,因此我的ajaxComplete回调从未被调用,而是我得到了已经重定向的登录页面响应,这显然是一个status 200.问题:如何检测成功的200响应是您的实际登录页面还是其他任意页面?

解决方案

由于我无法捕获302重定向响应,因此我LoginPage在登录页面上添加了一个标题,其中包含登录页面本身的URL.在模块中,我现在监听标题并进行重定向:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

......这就像魅力:).你可能想知道为什么我在LoginPage标题中包含url ...好吧基本上因为我发现无法确定GETxhr对象的自动位置重定向产生的url ...


@ShaquinTrifonoff不再了.我没有使用X-前缀,因为在[2011年6月](http://tools.ietf.org/html/draft-saintandre-xdash-00)中,ITEF文件提出了他们的弃用,事实上,[2012年6月] (http://www.ietf.org/rfc/rfc6648.txt)自定义标题不应该以"X-"为前缀,这不是官方的.

8> jwaliszko..:

我知道这个话题很老,但我会给出另一种方法,我已经找到并在此处描述过.基本上我正在使用带有WIF的ASP.MVC (但这对于本主题的上下文并不重要 - 无论使用哪个框架,答案都是足够的.线索保持不变 - 在执行ajax请求时处理与身份验证失败相关的问题).

下面显示的方法可以应用于开箱即用的所有ajax请求(如果他们没有在发送事件之前重新定义).

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

在执行任何ajax请求之前,CheckPulse调用方法(控制器方法可以是最简单的):

[Authorize]
public virtual void CheckPulse() {}

如果用户未经过身份验证(令牌已过期),则无法访问此类方法(受Authorize属性保护).因为框架处理身份验证,所以当令牌过期时,它会将http状态302置于响应中.如果您不希望浏览器透明地处理302响应,请在Global.asax中捕获它并更改响应状态 - 例如200 OK.此外,添加标头,指示您以特殊方式处理此类响应(稍后在客户端):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

最后在客户端检查这样的自定义标头.如果存在 - 应该完成到登录页面的完全重定向(在我的情况下window.location,由我的框架自动处理的请求中的url替换).

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}



9> Tyr..:

我这样解决了这个问题:

添加中间件以处理响应,如果它是ajax请求的重定向,则使用重定向URL将响应更改为正常响应.

class AjaxRedirect(object):
  def process_response(self, request, response):
    if request.is_ajax():
      if type(response) == HttpResponseRedirect:
        r = HttpResponse(json.dumps({'redirect': response['Location']}))
        return r
    return response

然后在ajaxComplete中,如果响应包含重定向,则它必须是重定向,因此请更改浏览器的位置.

$('body').ajaxComplete(function (e, xhr, settings) {
   if (xhr.status == 200) {
       var redirect = null;
       try {
           redirect = $.parseJSON(xhr.responseText).redirect;
           if (redirect) {
               window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
           }
       } catch (e) {
           return;
       }
   }
}



10> rynop..:

我认为更好的方法是利用现有的HTTP协议响应代码401 Unauthorized.

这是我解决它的方式:

    服务器端:如果会话到期,请求是ajax.发送401响应代码头

    客户端:绑定到ajax事件

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });
    

IMO这是更通用的,你不是在写一些新的自定义规范/标题.您也不必修改任何现有的ajax调用.

编辑: Per @ Rob的评论如下,401(认证错误的HTTP状态代码)应该是指标.有关更多详细信息,请参阅403 Forbidden vs 401 Unauthorized HTTP响应.有人说,一些Web框架使用403进行身份验证和授权错误 - 因此相应地进行调整.谢谢Rob.



11> Rob..:

大多数给定的解决方案使用解决方法,使用额外的标头或不适当的HTTP代码.这些解决方案很可能会起作用,但感觉有点'hacky'.我想出了另一个解决方案.

我们正在使用配置为在401响应上重定向(passiveRedirectEnabled ="true")的WIF.重定向在处理正常请求时很有用,但不适用于AJAX请求(因为浏览器不会执行302 /重定向).

使用global.asax中的以下代码,您可以禁用AJAX请求的重定向:

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

这允许您返回AJAX请求的401响应,然后您的javascript可以通过重新加载页面来处理.重新加载页面将抛出401将由WIF处理(WIF将用户重定向到登录页面).

一个示例javascript来处理401错误:

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});



12> morten.c..:

我发现的另一个解决方案(如果要设置全局行为,尤其有用)是将该$.ajaxsetup()方法与statusCode属性一起使用.像其他人指出的那样,不要使用重定向statuscode(3xx),而是使用状态代码4xx并处理重定向客户端.

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

替换400为您要处理的状态代码.就像已经提到的那样401 Unauthorized可能是一个好主意.我使用400它,因为它非常不明确,我可以使用401更具体的情况(如错误的登录凭据).因此,4xx当会话超时并且您处理重定向客户端时,您的后端应该返回错误代码,而不是直接重定向.即使使用像backbone.js这样的框架,也适合我



13> Przemek Marc..:

然后可能会出现此问题使用ASP.NET MVC RedirectToAction方法.为了防止表单在div中显示响应,您可以简单地使用某种类型的ajax响应过滤器来使用$ .ajaxSetup进行响应.如果响应包含MVC重定向,则可以在JS端评估此表达式.JS的示例代码如下:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

如果数据为:"window.location ='/ Acount/Login'"上面的过滤器将捕获该值并进行评估以进行重定向,而不是让数据显示.



14> Timmerz..:

我有一个适合我的简单解决方案,无需更改服务器代码...只需添加一小杯肉豆蔻...

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

我检查是否存在html标记,但您可以更改indexOf以搜索登录页面中存在的任何唯一字符串...



15> Graham King..:

将弗拉基米尔·普鲁德尼科夫和托马斯·汉森所说的放在一起:

更改服务器端代码以检测它是否为XHR.如果是,则将重定向的响应代码设置为278.在django中:

   if request.is_ajax():
      response.status_code = 278

这使得浏览器将响应视为成功,并将其交给您的Javascript.

在您的JS中,确保表单提交是通过Ajax,检查响应代码并根据需要重定向:

$('#my-form').submit(function(event){ 

  event.preventDefault();   
  var options = {
    url: $(this).attr('action'),
    type: 'POST',
    complete: function(response, textStatus) {    
      if (response.status == 278) { 
        window.location = response.getResponseHeader('Location')
      }
      else { ... your code here ... } 
    },
    data: $(this).serialize(),   
  };   
  $.ajax(options); 
});



16> podeig..:

尝试

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

把它放在登录页面上.如果它被加载到主页面上的div中,它将重定向直到登录页面."#site"是div的id,它位于除登录页面之外的所有页面上.



17> 小智..:
    



18> John..:

虽然答案似乎适用于人们,如果您使用的是Spring Security,我发现扩展了LoginUrlAuthenticationEntryPoint并添加了特定代码来处理AJAX更强大.大多数示例都拦截了所有重定向,而不仅仅是身份验证失败.这对我工作的项目来说是不可取的.如果您不希望缓存失败的AJAX请求,您可能会发现还需要扩展ExceptionTranslationFilter并覆盖"sendStartAuthentication"方法以删除缓存步骤.

示例AjaxAwareAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (isAjax(request)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
        } else {
        super.commence(request, response, authException);
        }
    }

    public static boolean isAjax(HttpServletRequest request) {
        return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

来源: 1,2


如果反对派选民解释他们为什么反对投票,这将对我有帮助。如果此解决方案有不好的地方,我想从错误中学习。谢谢。

19> Paul Richard..:

我通过在login.php页面中添加以下内容解决了这个问题.




20> 小智..:

让我再次引用@Steg描述的问题

我遇到了类似的问题.我执行一个有两个可能响应的ajax请求:一个将浏览器重定向到一个新页面,另一个用新的替换当前页面上的现有HTML表单.

恕我直言,这是一个真正的挑战,必须正式扩展到当前的HTTP标准.

我相信新的Http标准将使用新的状态代码.意思是:目前301/302告诉浏览器去读取的内容这个请求到一个新的location.

在扩展标准中,它会说如果响应status: 308(只是一个例子),那么浏览器应该将主页面重定向到location提供的.

话虽如此; 我倾向于已经模仿这种未来行为,因此当需要document.redirect时,我让服务器响应为:

status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html

当JS获取" status: 204"时,它会检查x-status: 308标头是否存在,并对标题中提供的页面执行document.redirect location.

这对你有意义吗?



21> Tomer..:

有些人可能会发现以下有用:

我希望将客户端重定向到登录页面,以便在没有授权令牌的情况下发送任何休息操作.由于我的所有rest-actions都是基于Ajax的,因此我需要一种很好的通用方法来重定向到登录页面,而不是处理Ajax成功函数.

这就是我所做的:

在任何Ajax请求中,我的服务器将返回JSON 200响应"需要进行身份验证"(如果客户端需要进行身份验证).

Java中的简单示例(服务器端):

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

在我的Javascript中,我添加了以下代码:

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

这就是它.



22> 小智..:

你应该在servlet response.setStatus(response.SC_MOVED_PERMANENTLY); 中发送重定向所需的'301'xmlHttp状态...

并且在$ .ajax函数中你不应该使用.toString()函数....

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

问题是它不是很灵活,你无法决定你想要重定向的位置..

重定向通过servlet应该是最好的方法.但我仍然无法找到正确的方法.



23> Bretticus..:

我只想锁定整个页面的任何ajax请求.@SuperG让我开始了.这是我最终得到的:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

我想专门检查某些http状态代码以作出决定.但是,您可以绑定到ajaxError以获得除成功之外的任何其他内容(也许只有200个?)我本来可以写的:

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}



24> 小智..:

如果您还想传递值,那么您还可以设置会话变量并访问例如:在您的jsp中,您可以编写

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

然后你可以将这个临时值存储在你的javascript变量中并进行游戏



25> jocull..:

我没有使用标头解决方案取得任何成功 - 它们从未在我的ajaxSuccess/ajaxComplete方法中被选中.我在自定义响应中使用了Steg的答案,但我修改了JS方面的一些.我设置了一个我在每个函数中调用的方法,因此我可以使用标准$.get$.post方法.

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

它的使用示例......

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}



26> Ali Adlavara..:

最后,我通过添加自定义来解决问题HTTP Header.就在响应服务器端的每个请求之前,我将当前请求的URL添加到响应的头部.

我在服务器上的应用程序类型是Asp.Net MVC,它有一个很好的位置.在Global.asax我实施的Application_EndRequest事件所以:

    public class MvcApplication : System.Web.HttpApplication
    {

    //  ...
    //  ...

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
        }

    }

它对我来说很完美!现在的每一个响应JQuery $.post我有要求url,也这是作为结果等响应头POST的状态的方法302,303.......

其他重要的是没有必要修改服务器端和客户端的代码.

接下来就是能够以这种方式访问​​后期行动的其他信息,例如错误,消息和......

我贴了这个,也许帮助别人:)

推荐阅读
低调pasta_730
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有